Neo
Neo

Reputation: 4497

How to assign properties generically using Expression.Assign selector with target type of object?

I am trying to use an Expression selector to generically assign properties from one type of object to another where the properties are of various types. This is the code I have so far:

var type1 = new Type1();
var type2 = new Type2();

...

var propMap = new List<Tuple<Expression<Func<Type1, object>>, Expression<Func<TradeStaticAttributesItemModel, object>>>>
    {
        new Tuple<Expression<Func<Type1, object>>, Expression<Func<Type2, object>>>(x => x.Prop1, x => x.Prop1),
        new Tuple<Expression<Func<Type1, object>>, Expression<Func<Type2, object>>>(x => x.Prop2, x => x.Prop2)
    };

foreach (var prop in propMap)
{
    if (prop.Item1.Compile()(type1) != prop.Item2.Compile()(type2))
    {
        ParameterExpression valueParameterExpression = Expression.Parameter(prop.Item2.Body.Type);
        var assign = Expression.Lambda<Action<Type1, object>>(Expression.Assign(prop.Item1.Body, valueParameterExpression), prop.Item1.Parameters.Single(), valueParameterExpression);
        Action<Type1, object> setter = assign.Compile();
        setter(type1, prop.Item2.Compile()(type2));
    }
}

However, I am However, I'm getting the error "ParameterExpression of type 'System.String' cannot be used for delegate parameter of type 'System.Object'" when the type of property is a string. I guess this would also happen for any other type other than object. Any idea how to make this code work for such a situation?

I found a potential answer here using Expression.Convert, but I can't get it to work.

Upvotes: 2

Views: 1383

Answers (1)

Neo
Neo

Reputation: 4497

OK, I got this working with the following code:

var type1 = new Type1();
var type2 = new Type2();

...

var propMap = new List<Tuple<Expression<Func<Type1, object>>, Expression<Func<TradeStaticAttributesItemModel, object>>>>
    {
        new Tuple<Expression<Func<Type1, object>>, Expression<Func<Type2, object>>>(x => x.Prop1, x => x.Prop1),
        new Tuple<Expression<Func<Type1, object>>, Expression<Func<Type2, object>>>(x => x.Prop2, x => x.Prop2)
    };

foreach (var prop in propMap)
{
    if (prop.Item1.Compile()(type1) != prop.Item2.Compile()(type2))
    {
        ParameterExpression valueParameterExpression = Expression.Parameter(typeof(object));

        // This handles nullable types
        Expression targetExpression = prop.Item1.Body is UnaryExpression ? ((UnaryExpression)prop.Item1.Body).Operand : prop.Item1.Body;

        var assign = Expression.Lambda<Action<Type1, object>>(
            Expression.Assign(targetExpression, Expression.Convert(valueParameterExpression, targetExpression.Type)),
            prop.Item1.Parameters.Single(),
            valueParameterExpression);

        Action<Type1, object> setter = assign.Compile();
        setter(type1, prop.Item2.Compile()(type2));
    }
}

This works for nullable types (as commented). I now know Expression, but if anyone has any improvements for this code, please feel free to suggest them!

Upvotes: 4

Related Questions