Expression to create an instance with object initializer

agent47 picture agent47 · Oct 3, 2012 · Viewed 18k times · Source

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?

Answer

svick picture svick · Oct 3, 2012

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}