2008-11-04 7 views
39

Im Allgemeinen, wenn der bedingten Operator, hier ist die Syntax:Wie kann ich einen Func <> bedingt zwischen lambdas mit dem bedingten ternären Operator zuweisen?

int x = 6; 
int y = x == 6 ? 5 : 9; 

Nichts Besonderes, ziemlich geradlinig.

Versuchen wir nun, dies zu verwenden, wenn Sie einem Func-Typ ein Lambda zuweisen. Lassen Sie mich erklären:

Func<Order, bool> predicate = id == null 
    ? p => p.EmployeeID == null 
    : p => p.EmployeeID == id; 

Das ist die gleiche Syntax ist, und sollte Arbeit? Recht? Aus irgendeinem Grund nicht. Der Compiler gibt diese schöne kryptische Nachricht:

Fehler 1 Art des bedingten Ausdrucks nicht bestimmt werden kann, weil es keine implizite Konvertierung zwischen ‚Lambda-Ausdruck‘ ist und ‚Lambda-Ausdruck‘

Ich ging dann weiter und die Syntax geändert und auf diese Weise es Arbeit tat

Func<Order, bool> predicate = id == null 
    ? predicate = p => p.EmployeeID == null 
    : predicate = p => p.EmployeeID == id; 

ich nur neugierig bin, warum es nicht die erste Art und Weise arbeiten?

(Randbemerkung: Ich landete den Code nicht benötigen, wie ich das herausgefunden, wenn ein int Wert gegen null zu vergleichen, die Sie gerade object.Equals verwenden)

Antwort

40

Sie einen Lambda-Ausdruck auf ein bestimmtes Ziel konvertieren Delegate-Typ, aber um den Typ des Bedingungsausdrucks zu bestimmen, muss der Compiler den Typ des zweiten und des dritten Operanden kennen. Während sie beide nur "Lambda-Ausdruck" sind, gibt es keine Konvertierung von einem zum anderen, so dass der Compiler nichts Nützliches tun kann.

Ich würde nicht einen Auftrag vorschlagen, mit jedoch - eine Besetzung ist offensichtlich:

Func<Order, bool> predicate = id == null 
    ? (Func<Order, bool>) (p => p.EmployeeID == null) 
    : p => p.EmployeeID == id; 

Beachten Sie, dass es nur für einen Operanden zur Verfügung stellen müssen, so dass der Compiler die Konvertierung von den anderen durchführen kann Lambda-Ausdruck.

+0

Ich frage mich, wenn der Compiler dies ableiten kann: 'Func Prädikat = p => p.EmployeeID == id', wie kommt es Schwierigkeiten hat Folgern dieses:' Func Prädikat = id == null ? (Func ) (p => p.EmployeeID == null) : p => p.EmployeeID == id; '? Ich meine, es kennt den Typ, der für den zweiten und dritten Operanden benötigt wird, über die Deklaration des Prädikats. – GDS

+0

@ GDS: Es gibt eine implizite Konvertierung vom Lambda-Ausdruck in den Delegate-Typ, weshalb die erste Version funktioniert. Die Deklaration von "Prädikat" hat jedoch keinen Einfluss auf die Typinferenz für den bedingten Ausdruck. Die Sprachspezifikation besagt grundsätzlich, dass der Typ des Bedingungsausdrucks nur über die Operanden ableitbar sein muss. –

+0

Dann frage ich mich, warum der ternäre Operator eine solche Anforderung hätte. Es ist nur eine bedingte Zuweisung des zweiten oder dritten Operanden zu einer Variablen oder eine bedingte Auswertung eines Ausdrucks. Sofern ich etwas nicht verpasse, sollte jede Schlussfolgerung, die mit einer direkten Zuweisung oder Bewertung erzielt wird, auch mit der bedingten Zuweisung oder Bewertung plausibel sein. Wenn ein Operant mehrdeutig ist, könnte die Schlussfolgerung des anderen die Mehrdeutigkeit potenziell auflösen. Wenn Mehrdeutigkeit nicht aufgelöst werden kann, kann der Compiler wie im obigen Beispiel immer noch beschweren. Vielleicht ein ausgelassenes Feature ...? – GDS

6

Der C# -Compiler kann den Typ des erstellten Lambda-Ausdrucks nicht ableiten, da zuerst der Ternär und dann die Zuweisung verarbeitet wird. Sie könnten auch tun:

Func<Order, bool> predicate = 
    id == null ? 
     new Func<Order,bool>(p => p.EmployeeID == null) : 
     new Func<Order,bool>(p => p.EmployeeID == id); 

aber das saugt nur, Sie auch

Func<Order, bool> predicate = 
    id == null ? 
     (Order p) => p.EmployeeID == null : 
     (Order p) => p.EmployeeID == id; 
+2

Letzteres funktioniert nicht, weil der Compiler nicht weiß, ob er in einen Delegaten oder einen Ausdrucksbaum konvertiert (oder, zum Beispiel, ein Func was auch in Ordnung wäre). –

0

Lassen Sie mich mein eigenes Beispiel haben, da ich hatte das gleiche Problem versuchen könnte, auch (mit der Hoffnung, dass das Beispiel hilfreich für andere):

Meine Find Methode ist eine generische Methode, die Expression<Func<T, bool>> als Prädikat erhält und List<T> als Ausgabe gibt.
Ich wollte Länder finden, aber ich brauche alle von ihnen, wenn Sprache Liste leer war, und gefilterte Liste, wenn Sprache Liste gefüllt war. Zuerst habe ich den Code wie unten angegeben:

var countries= 
Find(languages.Any() 
    ? (country => languages.Contains(country.Language)) 
    : (country => true)); 

Aber genau erhalte ich die Fehlermeldung: there is no implicit conversion between lambda expression and lambda expression.

Das Problem war, dass wir haben hier nur zwei Lambda-Ausdrücke, und nichts anderes, zum Beispiel, was ist country => true genau? Wir müssen den Typ mindestens einen der Lambda-Ausdrücke bestimmen. Wenn nur einer der Ausdrücke bestimmt wird, wird der Fehler weggelassen. Aber für den Code besser lesbar machen, ich beide Lambda-Ausdrücke extrahiert und verwendet, um die Variable statt, wie unten:

Expression<Func<Country, bool>> getAllPredicate = country => true; 
    Expression<Func<Country, bool>> getCountriesByLanguagePredicate = country => languages.Contains(country.Language); 

    var countries= Find(languages.Any() 
         ? getCountriesByLanguagePredicate 
         : getAllPredicate); 

ich betonen, dass, wenn ich bestimmt nur eine von den Typ des Ausdrucks, wird der Fehler behoben werden.

Verwandte Themen