Search code examples
c#lambdaexpression-trees

Lambda expression to initialize class with parameter and properties


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?


Solution

  • 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;
    }