2016-10-21 2 views
2

Я следующее выражение, которое имеет тип Expression<Func<TDocument, object>>Преобразование выражений <Func <TDocument, объект >> для выражения <Func <TDocument, TOutput >>

x => x.Name 

Теперь, я не знаю, тип x.Name во время компиляции, но теперь я во время выполнения, так как он хранится в Type.

Как я могу преобразовать свое выражение типа Expression<Func<TDocument, TOutput>>, где TOutput - это Type и не известно во время компиляции?

+0

это полезно http://stackoverflow.com/questions/24906609/how-to-get-compile-time-type-of-a-variable –

+0

Извините, но это не так. Не показывает мне, как преобразовать выражение. – Complexity

ответ

3

Вы просто должны обернуть исходное выражение Body в выражении Convert, а затем перестроить свою лямбду. Вот как я бы это сделать, если я мог дженерики использование:

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 
    ); 
} 

Использование:

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

Нет дженерики

Если вы не можете использовать дженерики, потому что вы не знаете, возвращаемый тип во время компиляции, Expression.Lambda фактически предлагает неосновную перегрузку, которую вы можете использовать следующим образом:

Expression ConvertReturnValue<TInput>(Expression<Func<TInput, object>> inputExpression, Type returnType) 
{ 
    Expression convertedExpressionBody = Expression.Convert(inputExpression.Body, returnType); 

    return Expression.Lambda(convertedExpressionBody, inputExpression.Parameters); 
} 

Вышеупомянутое все еще возвращает Expression<Func<TInput, TReturn>> (upcast для не общего Expression). Вы можете обратное приведение его позже, если вам нужно:

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

Добавление

Обратите внимание, что для типов возврата структура, окончательное выражение может в конечном итоге выглядит так:

(TDocument d) => (int)(object)d.ID; 
+0

Это хорошая отправная точка. Спасибо за это. Тем не менее, ваш метод потребовал, чтобы я теперь «TReturn» во время компиляции. В моем вопросе сказано, что я этого не знаю, он хранится в типе. – Complexity

+0

@Complexity, правда, что. Я добавил не общее решение. –

+0

Спасибо за не-общее решение, но, похоже, не решает мою проблему, так как мне не нужно «выражение», а «выражение» », для которого я не знаю выход. Я буду использовать обобщенную реализацию с несколькими операторами if. Я должен поддерживать только несколько встроенных типов, таких как 'string',' int', ... – Complexity