2009-01-08 13 views
9

Es gibt 4 überladene Signaturen für Enumerable.SelectMany. Um es einfach zu machen, ignorieren wir die beiden Signaturen mit int Argument. So haben wir zwei Unterschriften für Select:Wie C# -Compiler wählen Sie SelectMany beim Übersetzen von LINQ-Ausdruck?

public static IEnumerable<TResult> SelectMany<TSource, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, IEnumerable<TResult>> selector 
) 

public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, IEnumerable<TCollection>> collectionSelector, 
    Func<TSource, TCollection, TResult> resultSelector 
) 

Meine Frage ist: wie C# -Compiler Select wählen, wenn LINQ Ausdruck Erweiterung Methodenaufruf zu übersetzen?

Grundsätzlich, wenn es mehrere von in LINQ Ausdruck gibt, wird SelectMany sein. Aber es scheint, dass der C# -Compiler nur die zweite Signatur auswählt. Die erste Signatur wird niemals verwendet.

 IEnumerable<int> en1 = Enumerable.Range(1, 3); 
     IEnumerable<double> en2 = new double[] { 1.0, 3.14 }; 

     IEnumerable<string> en3 = 
      from i1 in en1 
      from i2 in en2 
      select (i1 * i2).ToString(); 

     foreach (var i in en3) 
     { 
      Console.WriteLine(i); 
     } 

Mit Hilfe von Reflektor, kann ich über LINQ Ausdruck, dass in

en1.SelectMany<int, double, string>(delegate (int i1) { 
     return en2; 
    }, delegate (int i1, double i2) { 
     double CS$0$0000 = i1 * i2return CS$0$0000.ToString(); 
    }) 

übersetzt Obiges Beispiel 3 Arten umfasst. Daher ist es sinnvoll, die zweite SelectMany-Signatur auszuwählen. Für das folgende Beispiel ist jedoch nur ein Typ beteiligt, es wählt immer noch die zweite Signatur aus.

 IEnumerable<int> en4 = 
      from i1 in en1 
      from i2 in Enumerable.Range(0, i1) 
      select i2; 

Es in übersetzt:

en1.SelectMany<int, int, int>(delegate (int i1) { 
     return Enumerable.Range(0, i1); 
    }, delegate (int i1, int i2) { 
     return i2; 
    }) 

Also, ich kann nicht einen Fall feststellen, dass LINQ-Ausdruck in die erste Select Signatur übersetzt. Gibt es einen solchen Fall?

Wenn die erste SelectMany-Signatur nicht verwendet wird, dann existiert sie nur, weil sie in der funktionalen Programmierung BIND von monad ist?

Vielleicht kann die Frage sein: Warum haben wir 2 Signaturen von SelectMany?

Danke.

Antwort

5

Gemäß der C# -Spezifikation generiert der Compiler keinen Überladungsaufruf für die erste Version von SelectMany. Die erste Version von SelectMany ist nützlich, um eine Liste von Listen in eine einzige flache Liste zu reduzieren.

public IEnumerable<string> Example(IEnumerable<IEnumerable<string>> enumerable) { 
    return enumerable.SelectMany(x => x); 
} 

Es hat keine starke Entsprechung in einem Abfrageausdruck.

Weitere Informationen finden Sie in Abschnitt 7.15.2 der C# -Sprachspezifikation.

+1

Wirklich ... eine Leerfunktion, die etwas zurückgibt, und niemand kommentiert? –

5

Warum haben wir 2 Signaturen von SelectMany?

So kann ich die erste in meinem Code verwenden.

var orders = Customers.SelectMany(c => c.Orders) 
+0

Sicher. Das ist nur für die Bequemlichkeit. –

+3

Die erste Signatur von SelectMany ist isomorph zum monadischen Bind-Operator, geschrieben >> = in Haskell. Ein Grund dafür ist, die Grundlage von LINQ auf dem gut etablierten Rahmen von Monaden zu festigen. Diese Grundlage macht LINQ polymorph für alle Arten von Dingen wie zusammensetzbare Zustandsvermehrung, Ausnahmen, Fortsetzungen, Alternativen usw. anwendbar: alle Monaden. Siehe http://en.wikipedia.org/wiki/Monad_(funktionelle_Programmierung). –