简体   繁体   English

如何将表达式树编译成可调用的方法C#?

[英]How do I compile an Expression Tree into a callable method, C#?

I have an expression tree I have created by parsing an Xml using the expression class in C#. 我有一个表达式树,我通过使用C#中的表达式类解析Xml来创建。 See this question . 看到这个问题

I only have Add, Subtract, Divide, Multiply, Parameters, And and Or in my Expression Tree. 我只在表达式树中添加,减去,除以,乘,参数,和或。 Is there a way to convert this ExpressionTree into a callable method? 有没有办法将此ExpressionTree转换为可调用方法? ...or do I have to emit the IL manually? ...或者我必须手动发射IL吗?

Kind regards, 亲切的问候,

You need to create a lambda - ie 你需要创建一个lambda - 即

var lambda = Expression.Lambda<Func<float,int>>(body, param);
Func<float,int> method = lambda.Compile();
int v = method(1.0); // test

where "body" is your expression tree (taking a float, returning an int) involving the ParameterExpression param. 其中“body”是涉及ParameterExpression参数的表达式树(带浮点数,返回一个int)。

You might also find this and this helpful. 你也可以找到这个很有帮助。

Here's an example of both approaches. 以下是两种方法的示例。 If I have missed something, or you want more information, just let me know. 如果我遗漏了某些内容,或者您​​想了解更多信息,请告诉我。

static void Main()
{
    // try to do "x + (3 * x)"

    var single = BuildSingle<decimal>();
    var composite = BuildComposite<decimal>();

    Console.WriteLine("{0} vs {1}", single(13.2M), composite(13.2M));
}
// utility method to get the 3 as the correct type, since there is not always a "int x T"
static Expression ConvertConstant<TSource, TDestination>(TSource value)
{
    return Expression.Convert(Expression.Constant(value, typeof(TSource)), typeof(TDestination));
}
// option 1: a single expression tree; this is the most efficient
static Func<T,T> BuildSingle<T>()
{        
    var param = Expression.Parameter(typeof(T), "x");
    Expression body = Expression.Add(param, Expression.Multiply(
        ConvertConstant<int, T>(3), param));
    var lambda = Expression.Lambda<Func<T, T>>(body, param);
    return lambda.Compile();
}
// option 2: nested expression trees:
static Func<T, T> BuildComposite<T>()
{

    // step 1: do the multiply:
    var paramInner = Expression.Parameter(typeof(T), "inner");
    Expression bodyInner = Expression.Multiply(
        ConvertConstant<int, T>(3), paramInner);
    var lambdaInner = Expression.Lambda(bodyInner, paramInner);

    // step 2: do the add, invoking the existing tree
    var paramOuter = Expression.Parameter(typeof(T), "outer");
    Expression bodyOuter = Expression.Add(paramOuter, Expression.Invoke(lambdaInner, paramOuter));
    var lambdaOuter = Expression.Lambda<Func<T, T>>(bodyOuter, paramOuter);

    return lambdaOuter.Compile();
}

Personally, I would aim towards the first method; 就个人而言,我会瞄准第一种方法; it it both simpler and more efficient. 它既简单又有效。 This might involve passing the original parameter throughout a stack of nested code, but so be it. 这可能涉及将原始参数传递到整个嵌套代码堆栈中,但也就是这样。 I have got some code somewhere that takes the "Invoke" approach (composite), and re-writes the tree as the first approach (single) - but it is quite complex and long. 我有一些代码采用“调用”方法(复合),并重新编写树作为第一种方法(单一) - 但它非常复杂和冗长。 But very useful for Entity Framework (which doesn't support Expression.Invoke). 但对Entity Framework(不支持Expression.Invoke)非常有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM