Expression
Expressionの使い方の例が手に入った。
strong-typed reflection!
http://weblogs.asp.net/cazzu/archive/2006/07/06/Linq-beyond-queries_3A00_-strong_2D00_typed-reflection_2100_.aspx\\
メソッド名と引数の型が分かっていてMethodInfoを取得したい時に、 MethodInfo method = typeof(Mock).GetMethod("PublicMethodParameters", new Type[] { typeof(string), typeof(int) })); こう書くのではなく、 MethodInfo info = Reflector.Method<Mock, string, int>( (x, y, z) => x.PublicMethodParameters(y, z)); ラムダ式を使って直接呼び出す式を書く。MockクラスのPublicMethodParametersメソッドを呼び出している。 これを直接実行するのでなく、Expressionに変換し、コードの内容をガリガリ調べて、呼び出しているメソッドを調べて、そのMethodInfoを取ってくる。 これの何がいいかといえば、メソッド名が変わったり、引数の型が変わった場合にコンパイルエラーになってくれるということだ。IDEのリネームも上手くいく。文字列に直接メソッド名を書いたらなかなかこうはできない。 メソッド名と引数の型が分かっている時にMethodInfoが欲しくなるケースがほとんどないということをのぞけば、いい考え方だ。 これを応用すれば、ダイナミックインターフェース的なこともできる気がする。 Reflector.DynamicInvoke<TReturn>(object obj, Expression<Func<object, TReturn>>); こんな感じのメソッドを作って、 TReturn returnValue = Reflector.DynamicInvoke<TReturn>(obj, obj => (obj as IHoge).Method(hoge, hoge2)); objをinterfaceにキャストして、interfaceのメソッドを呼び出すラムダ式を引数として渡し、Expressionに変換する。Expressionを解析して、interfaceのメソッドシグネチャを取り出す。そしてリフレクションでそのメソッド名と引数の型を持ったメソッドを呼び出す。 こうすれば、objがinterfaceを実装していなくても、そのメソッドが定義さえされていれば、実行することが出来る。ダックタイピング的なことが出来るだろう。 「普通にinterfaceを実装せえよ」という突っ込みに一つも返す言葉がないということを除けば、まあ使えそうな気はする。 Expressionのポイントは、「コンパイルが通るものしか作れない」「作ったものを実行する際には自分で好きなように実行することを変えることができる。その際コードをデータとして利用できる」、といったところだろうか。ラムダ式をそのまま実行するのでなく、何らかの変換を施して別のことを実行することが出来る。
NyaRuRuさんの例:
http://d.hatena.ne.jp/NyaRuRu/20060802/p1
C#のジェネリックではT型に対しては演算子+が使えないけど、それを使えるようにする。そのためにNumber<T>クラスを作り、+がオーバーロードされていればそれを使い、なければExpression.Add(これが何をするのかは良く知らない)を用いて足し算を行うメソッドをoperator +に書いておく。 Number<T>のoperator +を実装する際に、T を直接+演算子を使って足し合わせることはジェネリックの制限で出来ない。その代わりに、Expressionを使って加算を行うコードを生成し、それをCompileしてデリゲートにする。リフレクションを使ってもいいけど、こっちの方が実行速度が速い(と思う)。 Expressionのポイントその2としては、「Compileすればリフレクションよりはやいコードになる」ということがある。リフレクションでメソッドを探すよりも、Expression Treeから直接MethodInfoとかを取り出したほうが速いかもしれない。しかし呼び出すメソッドが分かってないとラムダ式として書けないので使用するところがなかなかないというところはある。