表达式树和调用委托

所以我有一个delegate指向一些我在第一次创建delegate对象时实际上并不知道的函数。 稍后将该对象设置为某个函数。

然后我还想创建一个表达式树,用参数调用委托(为了这个问题,参数可以是5 )。 这是我正在努力的一点; 下面的代码显示了我想要的但它没有编译。

 Func func = null; Expression expr = Expression.Invoke(func, Expression.Constant(5)); 

对于这个例子我可以这样做(这很实用,因为我需要在运行时构建表达式树):

 Func func = null; Expression<Func> expr = () => func(5); 

这使expr成为:

 () => Invoke(value(Test.Program+c__DisplayClass0).func, 5) 

这似乎意味着要使用delegate func ,我需要生成value(Test.Program+c__DisplayClass0).func位。

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

我想你想要做的是使用委托的Target和Method属性来传递以创建一个Call表达式。 基于JulianR的样本,这就是它的样子:

 Action func = i => Console.WriteLine(i * i); var callExpr = Expression.Call(Expression.Constant(func.Target), func.Method, Expression.Constant(5)); var lambdaExpr = Expression.Lambda(callExpr); var fn = lambdaExpr.Compile(); fn(); // Prints 25 

好的,这表明它是如何完成的(但在我看来它非常不优雅):

 Func func = null; Expression> bind = (x) => func(x); Expression expr = Expression.Invoke(bind, Expression.Constant(5)); Expression> lambda = Expression.Lambda>(expr); Func 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过于复杂。 出于这个原因,我不认为这个答案是好的,但也许可以建立在它的基础之上?

这应该工作:

 Action 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>>(callExpr, param); var fn = lambdaExpr.Compile(); // Compile the expression tree so it can be executed fn(func); // Prints 25 

表达式可以是一个mindfuck,但请记住: 表达式总是由其他表达式构成 。 表达式是描述代码的其他表达式的树。 您不能像在示例中那样传递实际委托,您需要的是该委托的表达式,通过说表达式需要委托类型的参数。 然后你说你想在参数上调用一个方法,即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>(writeln, arg); var compiled = lambda.Compile(); compiled(5); // Prints 25 

我能想到的另一种方法是捕获一个在闭包中本地声明的委托,但我不知道该怎么做。

虽然其他答案提供了一些工作方式,但有一个较短的答案:

Expression.Invoke(Expression.Constant(my_delegate), parameter_for_delegate)

它既适用于引用静态方法的代理,也适用于没有更改的实例方法。