ExpressionTreeとClojure
コトの始まり
ふつーのDelegateでClojureが実現可能なのは、知ってたし、中でどーなってるのか大体理解してたけど、ExpressionTreeの場合、そもそも出来るのか?出来たとして、どーなるのか?て部分が気になったので何となく調べてみた。
使ったサンプルコード
以下は、利用したサンプルコード、身もふたも無いほどにシンプルw
using System; using System.Linq.Expressions; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { int EncX = 100; Expression<Func<int>> exp = () => EncX; EncX = 200; var body = exp.Compile(); Console.WriteLine("Return value:" + body().ToString()); EncX = 500; Console.WriteLine("Return value:" + body().ToString()); } } }
Outputはいかのとーり
ReturnValue:200 ReturnValue:500
Outputを見ればわかるとおり、Enclojure側の値が変化すれば、ちゃんと追随して変化してることがわかる。
で、ばらしてみた
ばらしてみたところ、予想通り、不可視のprivate classをコンパイラが作成して、EncXをそっち側に逃がしていた。ただ、AnonymousMethodでClojureの構築を行った場合、AnonymousMethod側にも、コンパイラが勝手に、ローカル変数を取って、そいつを参照させるみたいな形だったワケだけど、ExpressionTreeの場合はどーなってるんだろうという疑問が残る。
気になるので、ILで見てみると、以下の通り。(一部抜粋)
.locals init ( [0] class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`1<int32>> exp [1] class [mscorlib]System.Func`1<int32> body [2] class ConsoleApplication2.Program/<>c__DisplayClass0 CS$<>8__locals1 <-こいつが仕込みの隠しローカル [3] int32 CS$0$0000 ) //ここで隠しローカルを初期化してる。 IL_0000: newobj instance void ConsoleApplication2.Program/<>c__DisplayClass0::.ctor() IL_0005: stloc.2 IL_0006: nop IL_0007: ldloc.2 //ここで、EncX=100;としてる。 IL_0008: ldc.i4.s 100 IL_000a: stfld int32 class ConsoleApplication2.Program/<>c__DisplayClass0::EncX //指定した覚えの無いConstantExpressionを構築してる(仕込みその2) IL_000f: ldloc.2 IL_0010: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object) //↑で作ったConstantExpression(中身は自動生成されたクラス)のEncXに相当するフィールドに対するMemberExpressionを構築してる。 IL_0015: ldtoken int32 class ConsoleApplication2.Program/<>c__DisplayClass0::EncX IL_001a: call class [mscorlib]System.Reflection.FieldInfo [mscorlib]System.Reflection.FieldInfo::GetFieldFromHandle(valuetype [mscorlib]System.RuntimeFieldHandle) IL_001f: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Field (class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.FieldInfo) //以下略 } // End of method Program.Main
うまいことしてるなぁって思った。ConstantでTreeの中に取り込んでしまって、そいつに対するMemberAccessを構築してる。てっきり、Variable当たりでなんかしてるかなぁと思ったけど、そうするより遙かにスマートだし、どっちにしろ、VariableをAssignするとなると、右辺のExpressionが必要になり、なんとかして、取り込む必要がある。となれば当然ConstantExpressionが必要になるので、はなっからVariable使わないんだなぁと。IL眺めながら妙に納得してしまったりw
今回は、非常に単純なサンプルだったので、全ての場合がそうなるのかちょっと言い切れない部分は残りますが、一応調べたコトを備忘録的に。。。