こんにちは。阿部です。
オーバーロードとは
オーバーロードとは、あるクラスに同じ名前で引数の異なるメソッドを定義することです。
1
2
3
4
5
6
7
8
9
|
string OverloadedFunction(int value)
{
return "引数はint型です。";
}
string OverloadedFunction(double value)
{
return "引数はdouble型です。";
}
|
メソッド使用時には、引数の型に応じて、それぞれ異なる処理が実行されます。
1
2
3
4
5
6
7
8
9
|
string OverloadedFunction(int value)
{
return "引数はint型です。";
}
string OverloadedFunction(double value)
{
return "引数はdouble型です。";
}
|
戻り値が異なるだけではオーバーロードできない
一方、引数の型が同じで戻り値の型だけが異なる場合は、ビルドエラーとなります。
1
2
3
4
5
6
7
8
9
10
|
string MyFunction(int value)
{
return value.ToString();
}
// 引数の型が同じなのでビルドエラー
int MyFunction(int value)
{
return value;
}
|
型引数による分岐
どうしても、同じ引数で戻り値の型を分けたい場合は、型引数(<T>
)を使用することで対処可能です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public static T MyFunction<T>(int value)
{
object ret = null;
if (typeof(T) == typeof(int))
{
// intの場合の処理
ret = value;
}
else if (typeof(T) == typeof(string))
{
// stringの場合の処理
ret = value.ToString();
}
return (T)ret;
}
|
呼び出し側はこうなります。
1
2
|
int n= MyFunction<int>(23);// intの場合の処理が実行される
string s = MyFunction<string>(23); // stringの場合の処理が実行される
|
型引数を付けるのが面倒ですね。これなら、関数名をMyFunctionInt
、MyFunctionString
などとしてしまった方が良さそうです。
戻り値の型によるオーバーロードの実現
さて、ここからが本題です。型引数も無くしてしまって、次のような関数を作りましょう。
1
2
|
int n= MyFunction(23); // intの場合の処理が実行される
string s = MyFunction(23); // stringの場合の処理が実行される
|
これを実現するためには、まず、関数自体は次のように作ります。
1
2
3
4
|
MyFunctionResult MyFunction(int arg)
{
return new MyFunctionResult(arg);
}
|
戻り値はint
でもstring
でもなく、この関数専用の型(MyFunctionResult
)にします。そして、MyFunctionResult
をnew
して返すだけの処理とします。
さて、仕掛けは、このMyFunctionResult
型の方にあります。次のように作ります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class MyFunctionResult
{
// 引数を保存しておくためのフィールド
private int arg;
public MyFunctionResult(int arg)
{
this.arg = arg;
}
public static implicit operator int(MyFunctionResult value)
{
// int型の場合の処理
return value.arg;
}
public static implicit operator string(MyFunctionResult value)
{
// string型の場合の処理
return value.arg.ToString();
}
}
|
ポイントは、型変換演算子(public static implicit operator 型名
)です。このような演算子を定義しておくと、MyFunctionResult
型オブジェクトを他の型に変換することができます。
また、implicit
とは、「暗黙的」という意味で、これを指定しておけば、明示的にキャストしなくても必要に応じてMyFunctionResult
型から他の型への変換が行われるようになります。
図解
普通のオーバーロードだと、引数の型や個数が異なっている場合に、それぞれ異なる処理を行うことができます。
1
2
3
4
5
6
7
8
|
引数関数呼出し戻り値
+-----++-----+
|A| -------------------> |X|
+-----+OverloadedFunction+-----+
+-----++-----+
|B| -------------------> |Y|
+-----+OverloadedFunction+-----+
|
本手法だと、同じ型の引数に対して異なる処理を実行することができます。
1
2
3
4
5
6
7
8
|
引数関数呼出し戻り値型変換
+-----+ +------------------++-----+
|A| ------------> | MyFunctionResult | --+------> |X|
+-----+MyFunction +------------------+ | (X)+-----+
|
|+-----+
+------> |Y|
(Y)+-----+
|
使用法
代入先の型によって処理が分岐します。
1
2
|
int result1= MyFunction(23); // int用の処理が実行される
string result2 = MyFunction(23); // string用の処理が実行される
|
戻り値を、他の関数の引数に直接渡すような場合でも、型によって処理が分岐します。
1
2
3
4
5
6
7
8
9
|
// 例えばintを受け取る関数や、
void IntFunction(int value) { /* 省略 */}
// stringを受け取る関数が存在して、
void StrFunction(string value) { /* 省略 */}
// 件の関数を引数に渡すと……
IntFunction(MyFunction(23)); // int用の処理が実行される
StrFunction(MyFunction(23)); // string用の処理が実行される
|
型変換を定義していない型に代入しようとすると、ビルドエラーとなります。
1
2
3
|
int result1= MyFunction(23); // OK
string result2 = MyFunction(23); // OK
bool result3= MyFunction(23); // ビルドエラー
|