Reputation: 4497
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
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