Expression to create an instance with object initializer
Asked Answered
B

2

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?

Brainstorming answered 3/10, 2012 at 4:6 Comment(2)
Can you read my post : abhisheksur.com/2010/09/use-of-expression-trees-in-lamda-c.html to generate yourself?Goldengoldenberg
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?Brainstorming
D
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}
Digastric answered 3/10, 2012 at 9:15 Comment(2)
@Digastric thank you very very very much :D what I was looking for actually is MemberInit. thank you again. +1 and acceptBrainstorming
@Digastric would you please answer this question : #65435000Copyread
B
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.

Brainstorming answered 3/10, 2012 at 6:52 Comment(2)
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.Digastric
@Digastric thank you too much. I want to do compile and cache the func to feture using.Brainstorming

© 2022 - 2024 — McMap. All rights reserved.