Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 879
StrugglingCoder Avatar asked Dec 18 '22 11:12

StrugglingCoder


2 Answers

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.

like image 143
Sergey Kalinichenko Avatar answered Dec 24 '22 00:12

Sergey Kalinichenko


You can create your lambda expression like that:

LambdaExpression lb = Expression.Lambda(b4);

You can then compile this expression to a delegate:

Delegate dlg = lb.Compile();

And cast this delegate to a Func<int>:

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

And you can use this as usual:

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

The generic way works, too. Why do you use Func<int,int>? Your expression takes no input and returns a single int:

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

The generic argument leads to a LambdaExpression with a Compile method that returns a Func<int> instead of a Delegate that you'd need to cast again.

like image 21
René Vogt Avatar answered Dec 24 '22 01:12

René Vogt