C#中的表达式树

时间:2016-12-21 15:42:21

标签: c# .net lambda code-generation expression-trees

我第一次探索表达树。我有一些基本的疑虑。

基本上,表达式只接受lambda表达式。然后,我们可以将lambda表达式Compile()编译为MSIL代码,然后返回一个通用委托。我们可以按原样调用返回的委托。我的理解是否正确?

如果这是我想要实现的目标:((10*5)+(9/4))

BinaryExpression b1 = Expression.MakeBinary(ExpressionType.Multiply, Expression.Constant(10), Expression.Constant(5));//(10*5)
BinaryExpression b2 = Expression.MakeBinary(ExpressionType.Divide, Expression.Constant(9), Expression.Constant(4));//(9/4)
BinaryExpression b4 = Expression.MakeBinary(ExpressionType.Add, b1, b2);//((10*5)+(9/4))

所以此时我们已经制作了lambda expression body。现在将它变为full lambda expression我们需要调用

Console.WriteLine(Expression.Lambda<Func<int, int>>(b4).Compile());

我没有得到这一部分。这也行不通。

为何选择Func<int,int>

是否内部表达式只将int作为param,整个表达式将返回一个int?

显然这不起作用。生成的lambda是怎样的?

我正在全面了解情况?如何使这项工作?

3 个答案:

答案 0 :(得分:6)

Expression.Lambda<Func<int, int>>(b4).Compile()

Func<int,int>是lambdas的签名,它采用单个int参数,并返回int。你的lambda有不同的签名。

  

显然这不起作用。

您的lambda不接受任何参数,因此您需要Func<int>

  

生成的lambda是怎样的?

生成的lambda是一个可调用的对象。如果你想评估你得到的表达式,请投射并调用它,如下所示:

var compiledLambda = (Func<int>)Expression.Lambda<Func<int>>(b4).Compile();
Console.WriteLine(compiledLambda());
//                              ^^

以上打印52,正如所料。

Demo 1.

如果您想制作Func<int,int>,请在表达式中添加参数,例如将其(p*5)+(9/4)设为p int参数:

ParameterExpression p = Expression.Parameter(typeof(int));
BinaryExpression b1 = Expression.MakeBinary(ExpressionType.Multiply, p, Expression.Constant(5));//(p*5)
BinaryExpression b2 = Expression.MakeBinary(ExpressionType.Divide, Expression.Constant(9), Expression.Constant(4));//(9/4)
BinaryExpression b4 = Expression.MakeBinary(ExpressionType.Add, b1, b2);
var compiledLambda = (Func<int,int>)Expression.Lambda<Func<int,int>>(b4, new[] {p}).Compile();
Console.WriteLine(compiledLambda(10)); // Prints 52
Console.WriteLine(compiledLambda(8));  // Prints 42

Demo 2.

答案 1 :(得分:4)

您可以像这样创建lambda表达式:

LambdaExpression lb = Expression.Lambda(b4);

然后,您可以将此表达式编译为委托:

Delegate dlg = lb.Compile();

并将此委托投射到Func<int>

Func<int> f = (Func<int>)dlg;

你可以像往常一样使用它:

Console.WriteLine(f()); // 52

通用方式也有效。为什么 您使用Func<int,int>?您的表达式不接受任何输入并返回单个int

Func<int> f = Expression.Lambda<Func<int>>(b4);

通用参数会导致LambdaExpression使用Compile方法返回Func<int>而不是Delegate,您需要再次投射。

答案 2 :(得分:1)

使用:

Console.WriteLine(Expression.Lambda<Func<int, int>>(b4).Compile());

实际上是用Console.WriteLine(object)重载打印它,它会打印参数Type的名称。

自行:

Expression.Lambda<Func<int>>(b4).Compile();

仅编译lambda, 为您提供delegate - 而不是其调用的结果

您需要 调用已编译的lambda 并仅打印结果:

Func<int> result = (Func<int>)Expression.Lambda<Func<int>>(b4).Compile();
Console.WriteLine(result());

另请注意,您尝试将delegate编译为Func<int,int>,其中包含int个参数并提供int结果,但您的代码却没有需要参数,因此您需要使用Func<int>