12

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, Parameters, And and Or in my Expression Tree. Is there a way to convert this ExpressionTree into a callable method? ...or do I have to emit the IL manually?

Kind regards,

Community
  • 1
  • 1
SharePoint Newbie
  • 5,974
  • 12
  • 62
  • 103

2 Answers2

12

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • The problem is ((x + 2) + y)/z When there are more than one parameters part of different sub expressions in the tree, what do I do? – SharePoint Newbie Dec 06 '08 at 17:56
  • Multiple parameters are possible (it is a params array of ParameterExpression); for sub-expressions, you need to invoke the inner expression (Expression.Invoke?) – Marc Gravell Dec 06 '08 at 17:58
  • You can also build the entire tree into a single expression; this is slightly more efficient, but is more complex to do. – Marc Gravell Dec 06 '08 at 18:01
  • I was initially trying to build the entire tree into a single expression. Then could not figure out a way to get to the parameters in the sub-expressions. Any info on that will help. – SharePoint Newbie Dec 06 '08 at 18:03
  • 1
    If you have a single expression, you can use as many ParameterExpression instances as you need. For sub-expressions, you simply need to map things in the outermost expression to parameters in the *inner* expression. For example, you might need to create a new ParameterExpression for the outer... – Marc Gravell Dec 06 '08 at 18:05
  • ... and simply pass that into the parameters set when using Expression.Invoke - this then maps as the value into the inner expression (which must also be a LambdaExpression via Expression.Lambda) – Marc Gravell Dec 06 '08 at 18:06
  • If you have a concrete example, I can fill in some blanks - but I have to nip off for a few hours... back in a bit ;-p – Marc Gravell Dec 06 '08 at 18:06
10

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

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900