#blog2navi() *C#: dynamic によるジェネリック型の動的オーバロード [#s0b62411] 今更 C# 4.0 のお話です。 C#で、 @code{Execute}; というメソッドにある型の引数を渡した時、次のように動作して欲しいものとします。 +型が @code{string}; であれば @code{string}; 版のオーバロードを呼ぶ。 +そうではなく型が @code{IEnumerable<T>}; (@code{T}; はジェネリック型)であれば @code{IEnumerable<T>}; 版のオーバロードを呼ぶ。 +上記以外の場合は @code{object}; 版のオーバロードを呼ぶ。 直接 @code{Execute}; メソッドを呼び出すのであれば特に難しいことはありません。 :コード| #code(csharp){{ using System; using System.Collections.Generic; namespace sample { class Program { private static void Execute<T>(IEnumerable<T> values) { Console.WriteLine("IEnumerable<T> : T is " + typeof(T).Name); } private static void Execute(string value) { Console.WriteLine("string"); } private static void Execute(object value) { Console.WriteLine("object : " + value.GetType().Name); } static void Main(string[] args) { Execute(0); Execute(new Exception()); Execute("abc"); Execute(new List<int>()); Execute(new char[1]); } } } }} :出力| #pre{{ object : Int32 object : Exception string IEnumerable<T> : T is Int32 IEnumerable<T> : T is Char }} しかし、引数に渡す型がジェネリック型である場合は話が違ってきます。 :コード| #code(csharp){{ using System; using System.Collections.Generic; namespace sample { class Program { // 実装略(前述のコードと同じ) private static void Execute<T>(IEnumerable<T> values) { /* ... */ } private static void Execute(string value) { /* ... */ } private static void Execute(object value) { /* ... */ } private static void CallExecute<T>(T value) { // ジェネリック型 T の値に対してオーバロード解決を試みる Execute(value); } static void Main(string[] args) { CallExecute(0); CallExecute(new Exception()); CallExecute("abc"); CallExecute(new List<int>()); CallExecute(new char[1]); } } } }} :出力| #pre{{ object : Int32 object : Exception object : String object : List`1 object : Char[] }} 先述のコードと同じ結果を期待したのですが、引数の型に関わらず @code{object}; 版のオーバロードが呼び出されてしまっています。~ ジェネリック型 @code{T}; に対するオーバロード解決はコンパイル時に行われ、型制約が指定されていない以上 @code{object}; 版のオーバロードにしかマッチしないためです。 そこで、 @code{Execute}; メソッドの引数を @code{dynamic}; 型にキャストすることで、オーバロード解決を実行時に行うようにしてみます。 :コード(変更部分のみ)| #code(csharp,13-){{ private static void CallExecute<T>(T value) { // ジェネリック型 T の値に対して動的なオーバロード解決を試みる Execute((dynamic)value); } }} :出力| #pre{{ object : Int32 object : Exception string IEnumerable<T> : T is Int32 IEnumerable<T> : T is Char }} 見事に期待通りの動作をしてくれました。 このように、 @code{dynamic}; 型を使うことで、ジェネリック型の引数に対して動的なオーバロード解決を行うことが出来ます。~ もちろん実行時に解決するためのオーバヘッドはゼロではありませんが、 @code{is}; 演算子などで型を調べて @code{if}; 文などで分岐させるしかなかった C# 3.5 以前と比べれば相当マシになっていると言えるでしょう。 今回の内容は、C++のテンプレート型であればコンパイル時に行えることではありますが、そこは仕組みの違い上仕方ありません。 RIGHT:Category: [[[C#>ぼやきごと/カテゴリ/C#]]][[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]] - 2014-03-02 18:24:15 ---- RIGHT:&blog2trackback(); #comment(above) #blog2navi()