2016-11-19 7 views
3

Допустим, у меня есть следующие два выражения:Объединение двух выражений в трубопровод

Expression<Func<T, IEnumerable<TNested>>> collectionSelector; 
Expression<Func<IEnumerable<TNested>, TNested>> elementSelector; 

Есть ли способ «совместить» это для того, чтобы сформировать ниже:

Expression<Func<T, TNested>> selector; 
(?)

EDIT:

Производительность очень важна, поэтому я бы оценил оптимальное решение с очень небольшими накладными расходами, если это возможно.

Большое спасибо!

ответ

4
static Expression<Func<A, C>> Foo<A, B, C>(
Expression<Func<B, C>> f, 
Expression<Func<A, B>> g) 
{ 
    var x = Expression.Parameter(typeof(A)); 
    return Expression.Lambda<Func<A, C>>(
     Expression.Invoke(f, Expression.Invoke(g, x)), x); 
} 

К сожалению, я не могу получить доступ к компьютеру (это плохое решение в плане производительности). На самом деле, я думаю, что вы можете оптимизировать код через выражение вызова.

Другой способ кажется, что (использования вспомогательной функции):

Func<A, C> Foo<A,B,C>(Func<B, C> f, Func<A, B> g) 
{ 
    return (A x) => f(g(x)); 
} 

Затем вы должны создать трубопроводную Expression с помощью вызова Expression с функцией Foo. Как этот псевдо-код:

var expr1 = get expresstion<B,C> 
    var expr2 = get Expression<A, B> 
    var foo = get method info of Foo method 
    specialize method with generic types A, B, C (via make generic method) 
    return Expression.Call(foo, expr1, expr2); 
+0

Спасибо! Как я могу это оптимизировать? производительность имеет решающее значение в этом конкретном случае. –

+0

Я изменил ответ – LmTinyToon

+0

Второе предложенное решение создает делегаты и анонимные объекты, которые, я бы сказал, не очень хороши с точки зрения производительности .. или я чего-то не хватает? –

1

Другим решением является использование ExpressionVisitor заменить параметр в правом выражении со всем левым выражением, другими словами, вставлять левые один в правой.

Expression visitor будет довольно простым, добавьте необходимые данные в конструктор, переопределите один метод и все.

internal sealed class ParameterReplaceVisitor : ExpressionVisitor 
{ 
    private readonly ParameterExpression _searched; 
    private readonly Expression _replaced; 

    public ParameterReplaceVisitor(ParameterExpression searched, Expression replaced) 
    { 
     if (searched == null) 
      throw new ArgumentNullException(nameof(searched)); 
     if (replaced == null) 
      throw new ArgumentNullException(nameof(replaced)); 

     _searched = searched; 
     _replaced = replaced; 
    } 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     if (node == _searched) 
      return _replaced; 

     return base.VisitParameter(node); 
    } 
} 

Его можно довольно легко расширить, чтобы обрабатывать коллекции выражений в конструкторе, но я сохранил его.

Теперь вам просто нужно использовать его на телах выражений и построить новую лямбду.

private static Expression<Func<TIn, TOut>> Merge<TIn, TInter, TOut>(Expression<Func<TIn, TInter>> left, Expression<Func<TInter, TOut>> right) 
{ 
    var merged = new ParameterReplaceVisitor(right.Parameters[0], left.Body).Visit(right.Body); 

    var lambda = Expression.Lambda<Func<TIn, TOut>>(merged, left.Parameters[0]); 

    return lambda; 
} 

Я тестировал его на этот код:

Expression<Func<string, int>> l = s => s.Length + 5; 
Expression<Func<int, string>> r = i => i.ToString() + " something"; 

var merged = Merge(l, r); 

var res = merged.Compile()("test"); 

и результат, как и ожидалось: 9 something.

EDIT: Если вы беспокоитесь, почему вы используете выражения вместо простых Func? Тогда вы можете просто вызвать один за другим. Анализируются ли деревья выражений?

+0

Я не знаю, почему это решение не имеет UP. Это оно! Это потрясающе! Спасибо. – Gh61

 Смежные вопросы

  • Нет связанных вопросов^_^