1

Given this lambda

public static Expression<Func<int, int>> Add(int add)
{
    return x => x + add;
}

it creates the following debug output:

.Lambda #Lambda1<System.Func`2[System.Int32,System.Int32]>(System.Int32 $x) {
    $x + .Constant<ConsoleAppExpressionTest.Program+<>c__DisplayClass1_0>(ConsoleAppExpressionTest.Program+<>c__DisplayClass1_0).add
}

The debug view shows that the field add is addressed within a constant.

How to create the same lambda dynamically?

What i've tried:

public static Expression<Func<int, int>> Add(int add)
{
    var x = Expression.Parameter(typeof(int), "x");
    var expr = Expression.Add(x, Expression.Field(Expression.Constant(null), "add"));

    return Expression.Lambda<Func<int, int>>(expr, x);
}

leads into a System.ArgumentException: 'Instance field 'add' is not defined for type 'System.Object''.

While using the parameter directly as constant would just create a copy of the value: Expression.Constant(add)

1
  • 1
    You are getting slightly confused by the Field you see the compiler create. However, that is only because the compiler creates a closure for you. Here you can see what the compiler turns your original code into: sharplab.io/… Commented Apr 12, 2021 at 11:29

2 Answers 2

1

The trick to this is to use simple Expression.Constant(add) or Expression.Constant(add, typeof(int)) (it is a good idea to express the type explicitly, especially if the value could be null).


As for using the field approach: you cannot, and neither can the regular compiler in your first version. What the compiler does is generate a closure, and use that i.e.

var obj = new SomeGeneratedClosureType();
obj.add = add; // and now *all* mentions of "add" use obj.add instead

and uses that object as the constant in:

var expr = Expression.Add(x, Expression.Field(Expression.Constant(obj), "add"));

This SomeGeneratedClosureType is the ConsoleAppExpressionTest.Program+<>c__DisplayClass1_0 in your question code.

However, it is more efficient and more direct not to do this, and just use the value directly. The compiler can't do this because it needs to maintain certain guarantees about how the value behaves, that aren't possible if you just use the value directly.

Sign up to request clarification or add additional context in comments.

2 Comments

while Expression.Constant(add) will deliver a different result to what i need, this still answers my question with the information about the closure type. thank you!
@cyptus note: in most cases, an anonymous type will suffice to mimic a closure/capture generated type, i.e. var obj = new { add }
1

I think this is what you want :

    public static Expression<Func<int, int>> Add(int add)
    {

        var x = Expression.Parameter(typeof(int), "x");
        var addExpression = Expression.Constant(add);
        var expr = Expression.Add(x, addExpression);

        return Expression.Lambda<Func<int, int>>(expr, x);
    }

1 Comment

as i told in my question, the static value of Expression.Constant(add) is not what i need in this context. I want to achive a reference, like the example in the debug view did.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.