事実上、.NET 4.0 からは構文ツリーに進化したらしい。
例えば、こんな感じのプログラムを式ツリーで構築したいとする。
for (int i = 1; i <= 10; i++)
{
Console.WriteLine(i);
}
式ツリーでループを組むことはできるが for 文を直接組むことはできないので、こんな感じに変形する。
int i = 1;
while (true)
{
if (i <= 10)
{
Console.WriteLine(i);
}
else
{
break;
}
i++;
}
これを式ツリーで組むとこんな感じになる。
var breakLabel = Expression.Label();
var iParameter = Expression.Variable(typeof(int), "i");
var writeLineMethod = typeof(Console).GetMethod("WriteLine", new[] { typeof(int) });
var lambda = Expression.Lambda
(
Expression.Block
(
new[]
{
iParameter
},
Expression.Assign(iParameter, Expression.Constant(1)),
Expression.Loop
(
Expression.Block
(
Expression.IfThenElse
(
Expression.LessThanOrEqual(iParameter, Expression.Constant(10)),
Expression.Call(writeLineMethod, iParameter),
Expression.Break(breakLabel)
),
Expression.PostIncrementAssign(iParameter)
),
breakLabel
)
)
);
var @delegate = lambda.Compile();
@delegate.DynamicInvoke();
式ツリーを Compile すると DynamicMethod が内部で作られるわけだけど、式ツリーを使わず自分で直接 DynamicMethod を構築するとなると IL を組み立てていくことになるので一苦労。IL だから for 文どころかループもないので、ラベルと goto で…。
ちなみに、CompileToMethod を使用すれば動的クラスの静的メソッドも構築できる。残念ながら動的クラスのインスタンスメソッドを構築することはできない。
最後に、AST (IronPython 同梱の Microsoft.Dynamic.dll に含まれている) を使用すると for 文が簡単に作れるので結構すっきりする。
var writeLineMethod = typeof(Console).GetMethod("WriteLine", new[] { typeof(int) });
var builder = Utils.Lambda(typeof(void), string.Empty);
var iParameter = builder.Variable(typeof(int), "i");
builder.Body = Expression.Block
(
Expression.Assign(iParameter, Expression.Constant(1)),
Utils.Loop
(
Expression.LessThanOrEqual(iParameter, Expression.Constant(10)),
Expression.PostIncrementAssign(iParameter),
Expression.Call(writeLineMethod, iParameter),
null
)
);
var lambda = builder.MakeLambda();
var @delegate = lambda.Compile();
@delegate.DynamicInvoke();