Expression to create an instance with object initializer

19,991

Solution 1

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}

Solution 2

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.

Share:
19,991
amiry jd
Author by

amiry jd

My other SO-Profile: https://stackoverflow.com/users/974276/j-amiry I am a hard-working and innovative developer with near 18 years of experience mostly based on .NET (Framework &amp; Core) technology stack. For the last few years, alongside developing some awesome applications, I was focused on some non-coding tasks such as building up development teams, team leading, tech leading, problem solving, consulting, architecting, reviewing other's code, and mentoring. As a self-motivated fast-learner experienced code-guy, who has a proactive personality to step up to new challenges, I think I'm the man who can get the job done.

Updated on June 03, 2022

Comments

  • amiry jd
    amiry jd almost 2 years

    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?

  • svick
    svick over 11 years
    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.
  • amiry jd
    amiry jd over 11 years
    @svick thank you too much. I want to do compile and cache the func to feture using.
  • amiry jd
    amiry jd over 11 years
    @svick thank you very very very much :D what I was looking for actually is MemberInit. thank you again. +1 and accept
  • nAviD
    nAviD over 3 years
    @svick would you please answer this question : stackoverflow.com/questions/65434500/…