Code, der dem Schlüsselwort 'let' in verketteten LINQ-Erweiterungsmethodenaufrufen entspricht

191

Mit den Abfrageverständnisfunktionen der C # -Compiler können Sie Code wie folgt schreiben:

var names = new string[] { "Dog", "Cat", "Giraffe", "Monkey", "Tortoise" };
var result =
    from animalName in names
    let nameLength = animalName.Length
    where nameLength > 3
    orderby nameLength
    select animalName; 

Im obigen Abfrageausdruck letermöglicht das Schlüsselwort die Weitergabe eines Werts an die Operationen where und orderby ohne doppelte Aufrufe an animalName.Length.

Was ist der äquivalente Satz von LINQ-Erweiterungsmethodenaufrufen, mit denen erreicht wird, was das Schlüsselwort "let" hier bewirkt?

LBushkin
quelle
11
Zu Ihrer Information, die C # 3.0-Spezifikation erklärt jede Übersetzungsregel für das Abfrageverständnis in äußerst detaillierten Details.
Eric Lippert
17
und für diejenigen, die die Spezifikation schwer finden, deckt Jon Skeets C # in Depth sie ebenfalls ab ;-p
Marc Gravell
Die C # -Sprachspezifikationen sind herunterladbare Word-Dokumente, deren Inhalt nicht von Suchmaschinen indiziert wird und die online weder verlinkbar noch durchsuchbar sind. Es wäre eine große Hilfe, wenn die Spezifikationen online verfügbar wären.
Olivier Jacot-Descombes

Antworten:

249

Let hat keine eigene Operation; es huckepack weg von Select. Sie können dies sehen, wenn Sie "Reflektor" verwenden, um eine vorhandene DLL auseinander zu ziehen.

es wird so etwas sein wie:

var result = names
        .Select(animalName => new { nameLength = animalName.Length, animalName})
        .Where(x=>x.nameLength > 3)
        .OrderBy(x=>x.nameLength)
        .Select(x=>x.animalName);
Marc Gravell
quelle
4
Woah, ich wusste nicht, dass Sie mit dem neuen Operator so automatisch kapseln können.
David Pfeffer
19
Sie können auch die kleine Schaltfläche "Lambda" im Ergebnisbereich von LinqPad verwenden, um den generierten Code anzuzeigen, wenn Sie mit einem Abfragbaren Element beginnen. Mit anderen Worten, wenn Sie Ihre erste Zeile in var names = new string [] {"Dog", ...} ändern .AsQueryable (); Führen Sie dann das Ganze in LinqPad aus, klicken Sie auf die kleine Lambda-Schaltfläche. Sie sehen den generierten Code, der praktisch mit Marc's Antwort identisch ist.
Reb.Cabin
3
Ich musste die .Dump()Erweiterungsmethode in LinqPad verwenden, um das resultierende Lambda zu sehen.
Justanotherdev
88

Es gibt einen guten Artikel hier

Erstellt im Wesentlichen letein anonymes Tupel. Es ist äquivalent zu:

var result = names.Select(
  animal => new { animal = animal, nameLength = animal.Length })
.Where(x => x.nameLength > 3)
.OrderBy(y => y.nameLength)
.Select(z => z.animal);
Keltex
quelle
Ich zitiere den obigen Artikelit seems prudent to recommend against using the let keyword in cases where you do not need to transform a variable
JB. Mit Monica.
Ich zitiere es weiter:This could be considered a micro-optimisation
Monsignore
7

Es gibt auch eine .Let-Erweiterungsmethode in System.Interactive, deren Zweck jedoch darin besteht, einen Lambda-Ausdruck einzuführen, der in einem fließenden Ausdruck "inline" ausgewertet werden soll. Betrachten Sie beispielsweise (in LinqPad beispielsweise) den folgenden Ausdruck, der bei jeder Ausführung neue Zufallszahlen erstellt:

var seq = EnumerableEx.Generate(
    new Random(),
    _ => true,
    _ => _,
    x => x.Next());

Beachten Sie Folgendes, um zu sehen, dass jedes Mal neue Zufallsstichproben angezeigt werden

seq.Zip(seq, Tuple.Create).Take(3).Dump();

das erzeugt Paare, in denen links und rechts unterschiedlich sind. Gehen Sie wie folgt vor, um Paare zu erzeugen, bei denen links und rechts immer gleich sind:

seq.Take(3).ToList().Let(xs => xs.Zip(xs, Tuple.Create)).Dump(); 

Wenn wir Lambda-Ausdrücke direkt aufrufen könnten, könnten wir schreiben

(xs => xs.Zip(xs, Tuple.Create))(seq.Take(3).ToList()).Dump();

Wir können jedoch keine Lambda-Ausdrücke aufrufen, als wären sie Methoden.

Reb.Cabin
quelle
1

Informationen zu Code, der dem Schlüsselwort 'let' in verketteten LINQ-Erweiterungsmethodenaufrufen entspricht

Der obige Kommentar ist nicht mehr gültig

var x = new List<int> { 2, 3, 4, 5, 6 }.AsQueryable();
(from val in x
let val1 = val
let val2 = val + 1
where val2 > val1
select val
).Dump();

produziert

System.Collections.Generic.List`1[System.Int32]
.Select(
  val =>
     new
     {
         val = val,
         val1 = val
     }
)
.Select(
  temp0 =>
     new
     {
         temp0 = temp0,
         val2 = (temp0.val + 1)
     }
)
.Where(temp1 => (temp1.val2 > temp1.temp0.val1))
.Select(temp1 => temp1.temp0.val)

so mehrere letsind jetzt optimiert

Fabio Angela
quelle