Search code examples
c#.netlambdacode-generationexpression-trees

Expression Trees in C#


For the very first time I am exploring expression trees. I have a few basic doubts.

So essentially , an expression takes only a lambda expression . Ans then we can Compile() the lambda expression to MSIL code which in turn returns a generic delegate. We can invoke the returned delegate as it is . Is my understanding correct ?

If it is here is what I am trying to achieve: ((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))

So at this point we have made the lambda expression body . Now to turn it to a full lambda expression we need to call

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

I am not getting this part . And this does not work also .

Why this Func<int,int>?

Is it like the inner expressions will take only int as param and the entire expression will return an int?

Obviously this does not work. How the generated lambda looks like ?

I am getting the entire picture? How to make this work?


Solution

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

    Func<int,int> is a signature for lambdas that take a single int parameter, and return an int. Your lambda has a different signature.

    Obviously this does not work.

    Your lambda does not take any parameters, so you need Func<int> instead.

    How the generated lambda looks like?

    Generated lambda is a callable object. If you would like to evaluate the expression that you get back, cast and call it, like this:

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

    The above prints 52, as expected.

    Demo 1.

    If you would like to make a Func<int,int>, add a parameter to your expression, e.g. make it (p*5)+(9/4) where p is an int parameter:

    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.