Invoke
正確な署名を知らなければ使用できません。ただし、DynamicInvoke
たとえば次のように使用できます。
((Delegate)exp.Compile()).DynamicInvoke(d);
dynamic
上記の は目的を果たさないことにd
注意してくださいobject[]
。
もう少し複雑なもう 1 つのアプローチは、それを としてコンパイルし、式( )をFunc<object[]>
書き直して、(元の からの) ExpressionVisitor
"パラメータ n" を に置き換えることです。これは、コンパイルされたラムダを保存して積極的に再利用する場合に有利です。ただし、特定のシナリオでは、呼び出しごとにコンパイルしているため、これには利点がありません。exp
p[n]
p
ParameterExpression
n
ConstantExpression
n
以下に例を示しますが、これは主に、同様のシナリオで、コンパイルされたデリゲートが再利用される後の読者を対象としています。この書き直しの「利点」は、 の署名Delegate.DynamicInvoke
を保持しながら、 のパフォーマンスへの影響を回避できることです。ただし、これはデリゲートが複数回使用されている場合にのみ役立ちます。現時点では (呼び出しごとにコンパイルされます)、ここでの「作業」のほとんどは、式コンパイルと JIT コンパイルになります。object[] => object
Delegate.DynamicInvoke
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
static class Program {
static void Main() {
Expression<Func<int, float, double>> exp = (i, f) => i * f;
var func = CompileToBasicType(exp);
object[] args = { 3, 2.3F };
object result = func(args); // 6.9 (double)
}
static Func<object[], object> CompileToBasicType(LambdaExpression exp) {
ParameterExpression arg =
Expression.Parameter(typeof(object[]), "args");
Dictionary<Expression, Expression> lookup =
new Dictionary<Expression, Expression>();
int i = 0;
foreach (var p in exp.Parameters) {
lookup.Add(p, Expression.Convert(Expression.ArrayIndex(
arg, Expression.Constant(i++)), p.Type));
}
var body = Expression.Convert(
new ReplaceVisitor(lookup).Visit(exp.Body), typeof(object));
return Expression.Lambda<Func<object[], object>>(body, arg).Compile();
}
class ReplaceVisitor : ExpressionVisitor {
private readonly Dictionary<Expression, Expression> lookup;
public ReplaceVisitor(Dictionary<Expression, Expression> lookup) {
if (lookup == null) throw new ArgumentNullException("lookup");
this.lookup= lookup;
}
public override Expression Visit(Expression node) {
Expression found;
return lookup.TryGetValue(node, out found) ? found
: base.Visit(node);
}
}
}