Is there a way to convert the property selector Expression<Func<T, TProperty>>
to Expression<Func<object, object>>
and vice versa? I already know how to convert to Expression<Func<T, object>>
using...
Expression<Func<T, TProperty>> oldExp;
Expression.Lambda<Func<T, object>>(Expression.Convert(oldExp.Body, typeof(object)), oldExp.Parameters);
... but I need to effectively cast both, the argument and the result of the function, not just e.g replace them with a ExpressionVisitor
because they need to be casted back later.
You were correct that you need to use an ExpressionVisitor and ExpressionConvert.
Here are the two methods that you asked for (as well as some support methods):
public Expression<Func<object, object>> ConvertToObject<TParm, TReturn>(Expression<Func<TParm, TReturn>> input)
{
var parm = Expression.Parameter(typeof(object));
var castParm = Expression.Convert(parm, typeof(TParm));
var body = ReplaceExpression(input.Body, input.Parameters[0], castParm);
body = Expression.Convert(body, typeof(object));
return Expression.Lambda<Func<object, object>>(body, parm);
}
public Expression<Func<TParm, TReturn>> ConvertBack<TParm, TReturn>(Expression<Func<object, object>> input)
{
var parm = Expression.Parameter(typeof(TParm));
var castParm = Expression.Convert(parm, typeof(object));
var body = ReplaceExpression(input.Body, input.Parameters[0], castParm);
body = Expression.Convert(body, typeof(TReturn));
return Expression.Lambda<Func<TParm, TReturn>>(body, parm);
}
Expression ReplaceExpression(Expression body, Expression source, Expression dest)
{
var replacer = new ExpressionReplacer(source, dest);
return replacer.Visit(body);
}
public class ExpressionReplacer : ExpressionVisitor
{
Expression _source;
Expression _dest;
public ExpressionReplacer(Expression source, Expression dest)
{
_source = source;
_dest = dest;
}
public override Expression Visit(Expression node)
{
if (node == _source)
return _dest;
return base.Visit(node);
}
}
Sample usage:
Expression<Func<Customer, string>> expression = c => c.Name;
var convertedExpression = ConvertToObject<Customer, string>(expression);
var backExpression = ConvertBack<Customer, string>(convertedExpression);
Of course we can replace the original two methods with one method with more type parameters:
public Expression<Func<TTargetParm, TTargetReturn>> ConvertGeneric<TParm, TReturn, TTargetParm, TTargetReturn>(Expression<Func<TParm, TReturn>> input)
{
var parm = Expression.Parameter(typeof(TTargetParm));
var castParm = Expression.Convert(parm, typeof(TParm));
var body = ReplaceExpression(input.Body, input.Parameters[0], castParm);
body = Expression.Convert(body, typeof(TTargetReturn));
return Expression.Lambda<Func<TTargetParm, TTargetReturn>>(body, parm);
}
Sample usage:
Expression<Func<Customer, string>> expression = c => c.Name;
var convertedExpression = ConvertGeneric<Customer, string, object, object>(expression);
var backExpression = ConvertGeneric<object, object, Customer, string>(convertedExpression);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With