所以我有一个delegate
指向我在第一次创建delegate
对象时实际上并不知道的功能。稍后将该对象设置为某个函数。
我还想创建一个表达式树,用参数调用委托(为了这个问题,参数可以是5
)。这是我正在努力的一点点;下面的代码显示了我想要的内容,但它没有编译。
Func<int, int> func = null;
Expression expr = Expression.Invoke(func, Expression.Constant(5));
对于这个例子,我可以这样做(这很实用,因为我需要在运行时构建表达式树):
Func<int, int> func = null;
Expression<Func<int>> expr = () => func(5);
这使expr
成为:
() => Invoke(value(Test.Program+<>c__DisplayClass0).func, 5)
这似乎意味着要使用delegate
func
,我需要生成value(Test.Program+<>c__DisplayClass0).func
位。
那么,我怎样才能创建一个调用委托的表达式树?
答案 0 :(得分:13)
我认为您要做的是使用委托的Target和Method属性来传递以创建Call表达式。基于JulianR的样本,这就是它的样子:
Action<int> func = i => Console.WriteLine(i * i);
var callExpr = Expression.Call(Expression.Constant(func.Target), func.Method, Expression.Constant(5));
var lambdaExpr = Expression.Lambda<Action>(callExpr);
var fn = lambdaExpr.Compile();
fn(); // Prints 25
答案 1 :(得分:10)
好的,这表明可以如何完成(但在我看来它非常不优雅):
Func<int, int> func = null;
Expression<Func<int, int>> bind = (x) => func(x);
Expression expr = Expression.Invoke(bind, Expression.Constant(5));
Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(expr);
Func<int> compiled = lambda.Compile();
Console.WriteLine(expr);
func = x => 3 * x;
Console.WriteLine(compiled());
func = x => 7 * x;
Console.WriteLine(compiled());
Console.Read();
基本上我使用(x) => func(x);
来创建一个调用委托指向的函数。但是你可以看到expr
过于复杂。出于这个原因,我不认为这个答案很好,但也许可以建立在它上面?
答案 2 :(得分:2)
这应该有效:
Action<int> func = i => Console.WriteLine(i * i);
// If func is null like in your example, the GetType() call fails,
// so give it a body or use typeof if you know the type at compile time
var param = Expression.Parameter(func.GetType());
// Call the Invoke method on the delegate, which is the same as invoking() it
var callExpr = Expression.Call(param, func.GetType().GetMethod("Invoke"), Expression.Constant(5));
var lambdaExpr = Expression.Lambda<Action<Action<int>>>(callExpr, param);
var fn = lambdaExpr.Compile(); // Compile the expression tree so it can be executed
fn(func); // Prints 25
表达式可以是一个精神错误,但请记住:表达式总是由其他表达式构建。表达式是描述代码的其他表达式的树。你不能像在你的例子中那样传递实际的委托,你需要的是该委托的表达式,通过说表达式需要一个委托类型的参数。然后你说你想在参数上调用一个方法,即Invoke
方法,参数为'5'。之后的所有其他内容就是如果你想将表达式转换为可运行的代码,你可能会这样做。
我用.NET4运行它,但我希望我没有在.NET4中只混合使用表达式。
编辑回应PythonPower的评论:
我认为你想要的东西(不是作为参数传递委托)只能在委托本身被描述为表达式时才能完成,如下所示:
var arg = Expression.Parameter(typeof(int), "i");
var multiply = Expression.Multiply(arg, arg);
var writeln = Expression.Call(typeof(Console).GetMethod("WriteLine",
new[] { typeof(int) }), multiply);
var lambda = Expression.Lambda<Action<int>>(writeln, arg);
var compiled = lambda.Compile();
compiled(5); // Prints 25
我能想到的另一种方法是捕获在闭包中本地声明的委托,但我不知道该怎么做。
答案 3 :(得分:1)
虽然其他答案提供了一些可行的方法,但有一个简短的方法:
Expression.Invoke(Expression.Constant(my_delegate), parameter_for_delegate)
它既适用于引用静态方法的委托,也适用于不变的实例方法。