2016-11-19 3 views
3

Lassen Sie uns sagen, dass ich die beiden folgenden Ausdrücke haben:zwei Ausdrücke in eine Pipeline Kombination

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

Gibt es eine Möglichkeit, diese um zu „kombinieren“, die unten zu bilden:

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

EDIT:

Leistung ist sehr kritisch, so würde ich eine optimale Lösung mit sehr wenig Aufwand zu schätzen wissen, wenn möglich.

Vielen Dank!

Antwort

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

Leider kann ich nicht auf Computer zugreifen (es ist eine schlechte Lösung in Bezug auf die Leistung). Eigentlich denke ich, dass Sie den Code über Call Expression optimieren können.

Ein anderer Weg scheint, wie die (usage Hilfsfunktion):

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

Dann sollten Sie Pipeline Expression über Call Expression mit Foo-Funktion erstellen. Wie dieser Pseudo-Code:

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

Danke! Wie kann ich das optimieren? Leistung ist in diesem speziellen Fall kritisch. –

+0

Ich modifizierte Antwort – LmTinyToon

+0

Die zweite vorgeschlagene Lösung schafft Delegierte und anonyme Objekte, die ich würde sagen, ist nicht sehr gut in Bezug auf die Leistung .. oder fehlt mir etwas? –

1

Eine andere Lösung ist ExpressionVisitor zu verwenden, um die Parameter in der rechten Ausdruck mit der gesamten linken Ausdruck, mit anderen Worten zu ersetzen, die linke in die rechte einbetten.

Ausdruck Besucher wird ziemlich einfach sein, benötigte Daten zum Konstruktor hinzufügen, überschreiben eine Methode und das ist alles.

Es kann ziemlich einfach erweitert werden, um Sammlungen von Ausdrücken im Konstruktor zu behandeln, aber ich hielt es kurz.

Jetzt müssen Sie es nur für die Ausdruckskörper verwenden und neues Lambda erstellen.

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

Getestet habe ich es auf diesem Code:

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

und das Ergebnis ist wie erwartet: 9 something.

BEARBEITEN: Wenn Leistung ist Ihr Anliegen, warum verwenden Sie Ausdrücke anstelle von einfach Func s? Dann könntest du nur nacheinander anrufen. Werden die Expressionsbäume später analysiert?

+0

Ich weiß nicht, warum diese Lösung kein UP hat. Das ist es! Dies ist perfekt! Vielen Dank. – Gh61

Verwandte Themen