简体   繁体   中英

How do parameters and variables actually work in C# Expression trees?

I'm honestly just lost, I can't seem to make sense of how Expression tree syntax is supposed to function, and keep getting errors trying to create the simplest of lambdas.

Let's take a very basic function: you supply a number as a parameter, and it simply adds that to 0, yes it useless, but I can't even make that work:

public void AddNumber(int number)
    {
        var input = Expression.Parameter(typeof(int), "number");
        var result = Expression.Variable(typeof(int), "result");
        
        var addAssign = Expression.AddAssign(result, input);

        int myExpectedResult = Expression.Lambda<Func<int, int>>(addAssign, input).Compile()(number);
    }

I get an error because the variable result is created, but not defined, which makes sense. So I assume I need to assign some value to it to make it work:

public void AddNumber(int number)
    {
        var input = Expression.Parameter(typeof(int), "number");
        var result = Expression.Variable(typeof(int), "result");
        
        var addAssign = Expression.AddAssign(result, input);

        int myExpectedResult = Expression.Lambda<Func<int, int>>(addAssign, input).Compile()(number);
    }

Same error, I'm assuming because the assign isn't really taken into account as it is not used. But you also can't use that assign variable as a parameter for the AddAssign function, so it basically makes no difference.

Now, if I create a block expression to do both assignements in a row, this will get rid of the error, however, my "input" is now always set to 0:

        public void AddNumber(int number)
    {
        var input = Expression.Parameter(typeof(int), "number");
        var result = Expression.Variable(typeof(int), "result");

        var blockExpression = Expression.Block(new[] { input, result },
            Expression.Assign(result, Expression.Constant(0)),
            Expression.AddAssign(result, input));

        int myExpectedResult = Expression.Lambda<Func<int, int>>(blockExpression, input).Compile()(number);
    }

How are you actually supposed to simply add a parameter, create a variable, then assign a value to that variable to return it? I can't seem to find any tutorial that properly explains how to keep the link between parameters/variables, in different BlockExpressions.

Thanks a lot! Any help is appreciated!

You're very close, but AddAssign is += , so you were trying to make something that looks like

x => x += y

where y is not defined and += can't write to x .

Instead try using Expression.Add for + , and Expression.Constant for an integer value.

Something like the following should work:

var paramExp = Expression.Parameter(typeof(int), "x");
var intExp = Expression.Constant(5);

// x + 5
var addExp = Expression.Add(paramExp, intExp);

// x => x + 5
var result = Expression.Lambda<Func<int, int>>(addExp, paramExp).Compile()(3);

Assert.AreEqual(8, result);

When writing expression trees, I always found it easier if I first wrote my result expression in a human readable form before trying to write it using an expression tree.

So the first step is - what are you trying to get? Is it number => number + 0 ?

If that's the case, it is a lambda expression. What you want to do it start destructuring the result expression into smaller expression parts. In this case, we have the left side of the lambda expression which is number and the right side of the lambda expression which is number + 0 .

What do we see here? There is a parameter of type int called number , and there is a constant value of type int with value 0 . What else is here - an Add expression.

So you first define your parameter:
var numberParamer = Expression.Parameter(typeof(int), "number");

Then you define your constant:
var constant = Expression.Constant(0);

Then define the add expression:
var addExpression = Expression.Add(constant, numberParamer);

And finally, the lambda:

var lambda = Expression.Lambda<Func<int, int>>(
  addExpression, // the first argument represents right side of the lambda
  numberParamer // the second argument takes a variable number of values, 
                // depending on the number of your parameters on the left side of your lambda. In this case, one
);

Now you can call it like:
var result = lambda.Compile()(number) ;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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