22

Is there any way to create an instance of an object with object initializer with an Expression Tree? I mean create an Expression Tree to build this lambda:

// my class
public class MyObject {
    public bool DisplayValue { get; set; }
}

// my lambda:
var lambda = (Func<bool, MyObject>)
             (displayValue => new MyObject { DisplayValue = displayValue });

How can I create this lambda with an Expression Tree?

UPDATE:

I tryed myself and write following code:

    public static Func<bool, dynamic> Creator;

    static void BuildLambda() {
        var expectedType = typeof(MyObject);
        var displayValueParam = Expression.Parameter(typeof(bool), "displayValue");
        var ctor = Expression.New(expectedType);
        var local = Expression.Parameter(expectedType, "obj");
        var displayValueProperty = Expression.Property(ctor, "DisplayValue");

        var returnTarget = Expression.Label(expectedType);
        var returnExpression = Expression.Return(returnTarget,local, expectedType);
        var returnLabel = Expression.Label(returnTarget, Expression.Default(expectedType));

        var block = Expression.Block(
            new[] { local },
            Expression.Assign(local, ctor),
            Expression.Assign(displayValueProperty, displayValueParam),
            Expression.Return(Expression.Label(expectedType), local, expectedType),
            returnExpression,
            returnLabel
            );
        Creator =
            Expression.Lambda<Func<bool, dynamic>>(block, displayValueParam)
                .Compile();
    }

But it throws the following error:

Cannot jump to undefined label ''.

Can everybody help me please?

amiry jd
  • 27,021
  • 30
  • 116
  • 215
  • Can you read my post : http://www.abhisheksur.com/2010/09/use-of-expression-trees-in-lamda-c.html to generate yourself? – abhishek Oct 03 '12 at 04:08
  • Thanks to link, it seems a greate aricle. But unfortunately I'm a new guy in expression and your article is very weighty. Can you post your suggestion please? – amiry jd Oct 03 '12 at 04:26

2 Answers2

62

To represent object initializers in an Expression, you should use Expression.MemberInit():

Expression<Func<bool, MyObject>> BuildLambda() { 
    var createdType = typeof(MyObject);
    var displayValueParam = Expression.Parameter(typeof(bool), "displayValue"); 
    var ctor = Expression.New(createdType);
    var displayValueProperty = createdType.GetProperty("DisplayValue");
    var displayValueAssignment = Expression.Bind(
        displayValueProperty, displayValueParam);
    var memberInit = Expression.MemberInit(ctor, displayValueAssignment);

    return
        Expression.Lambda<Func<bool, MyObject>>(memberInit, displayValueParam);
}

To verify this actually does what you want, you can call ToString() on the created expression. In this case, the output is as expected:

displayValue => new MyObject() {DisplayValue = displayValue}
svick
  • 236,525
  • 50
  • 385
  • 514
  • @svick thank you very very very much :D what I was looking for actually is `MemberInit`. thank you again. +1 and accept – amiry jd Oct 03 '12 at 14:31
  • @svick would you please answer this question : https://stackoverflow.com/questions/65434500/c-sharp-dynamically-generate-expression-of-property-and-empty-argument – nAviD Dec 24 '20 at 07:00
4

Finally I found my answer:

public static Func<bool, dynamic> Creator; 

static void BuildLambda() { 
    var expectedType = typeof(MyObject); 
    var displayValueParam = Expression.Parameter(typeof(bool), "displayValue"); 
    var ctor = Expression.New(expectedType); 
    var local = Expression.Parameter(expectedType, "obj"); 
    var displayValueProperty = Expression.Property(local, "DisplayValue"); 

    var returnTarget = Expression.Label(expectedType); 
    var returnExpression = Expression.Return(returnTarget,local, expectedType); 
    var returnLabel = Expression.Label(returnTarget, Expression.Default(expectedType)); 

    var block = Expression.Block( 
        new[] { local }, 
        Expression.Assign(local, ctor), 
        Expression.Assign(displayValueProperty, displayValueParam), 
        /* I forgot to remove this line:
         * Expression.Return(Expression.Label(expectedType), local, expectedType), 
         * and now it works.
         * */
        returnExpression, 
        returnLabel 
        ); 
    Creator = 
        Expression.Lambda<Func<bool, dynamic>>(block, displayValueParam) 
            .Compile(); 
}

UPDATE:

While it works fine, but @svick provide a better and shorter way in his answer that is actuallt wath I was looking for: MemberInit. Please see @svick's answer.

amiry jd
  • 27,021
  • 30
  • 116
  • 215
  • If all you want to do is to compile and execute the code, this will work well. But if you wanted to use the expression in some other way (e.g. in LINQ to SQL), this might not work well. What should work is to use `MemberInit()`, as in my answer. This way you will also get shorter, more readable code. – svick Oct 03 '12 at 09:19
  • @svick thank you too much. I want to do compile and cache the func to feture using. – amiry jd Oct 03 '12 at 14:20