C# 表达式树和调用委托
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2215712/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Expression Trees and Invoking a Delegate
提问by
So I have a delegate
which points to some function which I don't actually know about when I first create the delegate
object. The object is set to some function later.
所以我有一个delegate
指向某个函数,当我第一次创建delegate
对象时,我实际上并不知道这些函数。该对象稍后会设置为某个功能。
I also then want to make an expression tree that invokes the delegate with an argument (for this question's sake the argument can be 5
). This is the bit I'm struggling with; the code below shows what I want but it doesn't compile.
然后我还想制作一个表达式树,它用一个参数调用委托(为了这个问题,参数可以是5
)。这是我正在努力解决的问题;下面的代码显示了我想要的但它没有编译。
Func<int, int> func = null;
Expression expr = Expression.Invoke(func, Expression.Constant(5));
For this example I could do (this is practical since I need to build the expression trees at runtime):
对于这个例子,我可以做(这很实用,因为我需要在运行时构建表达式树):
Func<int, int> func = null;
Expression<Func<int>> expr = () => func(5);
This makes expr
become:
这使得expr
成为:
() => Invoke(value(Test.Program+<>c__DisplayClass0).func, 5)
Which seems to mean that to use the delegate
func
, I need to produce the value(Test.Program+<>c__DisplayClass0).func
bit.
这似乎意味着要使用delegate
func
,我需要生成value(Test.Program+<>c__DisplayClass0).func
位。
So, how can I make an expression tree which invokes a delegate?
那么,如何制作一个调用委托的表达式树呢?
采纳答案by Daniel Plaisted
OK, this shows how it canbe done (but it is very inelegant in my opinion):
OK,这说明它如何能够做到(但它是非常不雅的在我看来):
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();
Essentially I use (x) => func(x);
to make a function that calls what the delegate points to. But you can see that expr
is overly complicated. For this reason I don't consider this answer good, but maybe it can be built upon?
本质上,我(x) => func(x);
用来创建一个函数来调用委托所指向的内容。但是你可以看到这expr
过于复杂。出于这个原因,我不认为这个答案很好,但也许可以建立在它的基础上?
回答by JulianR
This should work:
这应该有效:
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
Expressions can be a mindfwor, but remember: expressions are always built up out of other expressions. An expression is a tree of other expressions that describes code. You can't pass in the actual delegate like you do in your example, what you need is an expression of that delegate, by saying the expression expects a parameter of the type of your delegate. Then you say you want to call a method on that parameter, namely the Invoke
method, with the argument '5'. All the other stuff after that is just if you want to turn the expression into runnable code, which you probably do.
表达式可能很麻烦,但请记住:表达式总是由其他表达式构建而成。表达式是描述代码的其他表达式的树。您不能像在示例中那样传入实际的委托,您需要的是该委托的表达式,即该表达式需要您的委托类型的参数。然后你说你想在那个参数上调用一个方法,即Invoke
方法,参数为“5”。之后的所有其他内容只是如果您想将表达式转换为可运行的代码,您可能会这样做。
I ran this with .NET4 though, I hope I haven't mixed in .NET4 only expression stuff.
虽然我用 .NET4 运行了这个,但我希望我没有混入 .NET4 只表达的东西。
EDIT In response to PythonPower's comment:
编辑响应 PythonPower 的评论:
I think what you want (not passing in the delegate as an argument) can only be done when the delegate itself is described as an expression, like this:
我认为您想要的(不将委托作为参数传递)只能在委托本身被描述为表达式时才能完成,如下所示:
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
The only other way I can think of is to capture an delegate declared locally in a closure, but I wouldn't know how to do that.
我能想到的唯一另一种方法是捕获在闭包中本地声明的委托,但我不知道该怎么做。
回答by Daniel Plaisted
I think what you want to do is use the Target and Method properties of the delegate to pass to create a Call expression. Building on JulianR's sample, this is what it would look like:
我想你想要做的是使用委托的 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
回答by Filip Navara
While other answers provide some working ways there's a shorter one:
虽然其他答案提供了一些工作方法,但有一个较短的方法:
Expression.Invoke(Expression.Constant(my_delegate), parameter_for_delegate)
Expression.Invoke(Expression.Constant(my_delegate), parameter_for_delegate)
It works both for delegates referencing static methods and instance methods with no change.
它既适用于引用静态方法的委托,也适用于没有更改的实例方法。