Lambda expression to initialize class with parameter and properties

c# expression-trees lambda

Question

I have a method that is used to generate a NewExpression.

public NewExpression CreateNewExpression( Expression<Func<T>> expression )
{
    return expression.Body as NewExpression;
}

You can then do stuff like this.

CreateNewExpression( () => new MyType() );

This is later on used to create a delegate the can be executed to create an object.

var body = Expression.MemberInit( constructorExpression, bindings );
var funcType = typeof( Func<> ).MakeGenericType( objectType );
var lambda = Expression.Lambda( funcType, body ).Compile();

This works great, but now I would like to pass some data into the method so it can be used in the construction of the object.

public NewExpression CreateNewExpression( Expression<Func<T, Data>> expression )

This would then be used like this.

CreateNewExpression( data => new MyType( data.Id ) );

I can't for the life of me figure out how to create the proper expression to pass the data in for the constructor to use.

If I don't extract the body from the expression in CreateNewExpression and do Expression.Invoke, I don't know how to set the properties to it after.

If I extract the body, I can set the properties using Expression.MemberInit, but then I can't figure out how to pass the data to it.

How can I do this?

Accepted Answer

When you want to use an expression that contains ParameterExpression, you need to reuse that same ParameterExpression in the final expression too.

To do this, you can access the Parameters of the original expression and then pass the parameter to Expression.Lambda(). The result could look something like this:

public Delegate M<T>(Expression<Func<Data, T>> expression)
{
    var objectType = typeof(T);

    var constructorExpression = (NewExpression)expression.Body;
    var constructorParameterExpression = expression.Parameters.Single();

    var bindings = …;

    var body = Expression.MemberInit( constructorExpression, bindings );
    var funcType = typeof( Func<,> ).MakeGenericType( typeof(Data), objectType );
    var lambda = Expression.Lambda( funcType, body, constructorParameterExpression )
        .Compile();

    return lambda;
}

Popular Answer

I think what could be the answer to your problem is Expression.Parameter.

I found a blog entry on geeks with blogs which utilizes this. The only downside of this solution is that you'd have to create new overloads for when you need more than three arguments. But I guess this could be easily solved with params object[].



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why