表达式树和调用委托

时间:2010-02-07 02:50:49

标签: c# .net lambda delegates expression-trees

所以我有一个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位。

那么,我怎样才能创建一个调用委托的表达式树?

4 个答案:

答案 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)

它既适用于引用静态方法的委托,也适用于不变的实例方法。