Complexity Complexity - 1 month ago 6
C# Question

Convert Expression<Func<TDocument, object>> to Expression<Func<TDocument, TOutput>>

I have the following expression which is of type

Expression<Func<TDocument, object>>


x => x.Name


Now, I don't know the type of
x.Name
at compile time, but I now it at runtime since it's stored in a
Type
.

How can I convert my expression to be of type
Expression<Func<TDocument, TOutput>>
where
TOutput
is a
Type
and not known at compile time?

Answer

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;
Comments