You just need to wrap the original expression's Body
in a Convert
expression and then rebuild your lambda. Here's how I would do it if I could use generics:
Expression<Func<TInput, TReturn>> ConvertReturnValue<TInput, TReturn>(
Expression<Func<TInput, object>> inputExpression)
{
Expression convertedExpressionBody = Expression.Convert(
inputExpression.Body, typeof(TReturn)
);
return Expression.Lambda<Func<TInput, TReturn>>(
convertedExpressionBody, inputExpression.Parameters
);
}
Usage:
Expression<Func<TDocument, object>> inputExpression = d => d.Name;
Expression<Func<TDocument, string>> convertedExpression
= ConvertReturnValue<TDocument, string>(inputExpression);
// Test.
TDocument doc = new TDocument { Name = "Zzz" };
string name = convertedExpression.Compile().Invoke(doc);
Assert.Equal("Zzz", name);
No generics
If you cannot use generics because you don't know the return type at compile time, Expression.Lambda
actually offers a non-generic overload, which you can use like this:
Expression ConvertReturnValue<TInput>(Expression<Func<TInput, object>> inputExpression, Type returnType)
{
Expression convertedExpressionBody = Expression.Convert(inputExpression.Body, returnType);
return Expression.Lambda(convertedExpressionBody, inputExpression.Parameters);
}
The above still returns an Expression<Func<TInput, TReturn>>
(upcast to a non-generic Expression
). You can downcast it later if you need to:
Expression<Func<TDocument, object>> inputExpression = d => d.Name;
Expression<Func<TDocument, string>> convertedExpression
= (Expression<Func<TDocument, string>>)ConvertReturnValue(inputExpression, typeof(string));
// Test.
TDocument doc = new TDocument { Name = "Zzz" };
string name = convertedExpression.Compile().Invoke(doc);
Assert.Equal("Zzz", name);
Addendum
Note that for struct return types, the final expression may end up looking like this:
(TDocument d) => (int)(object)d.ID;