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

后端 未结 2 1724
青春惊慌失措
青春惊慌失措 2020-12-09 05:23

I have an expression tree I have created by parsing an Xml using the expression class in C#. See this question.

I only have Add, Subtract, Divide, Multiply, Paramete

相关标签:
2条回答
  • 2020-12-09 06:11

    You need to create a lambda - i.e.

    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.

    You might also find this and this helpful.

    0 讨论(0)
  • 2020-12-09 06:14

    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).

    0 讨论(0)
提交回复
热议问题