Kod równoważny słowu kluczowemu „let” w połączonych połączeniach metod rozszerzenia LINQ

192

Korzystając z funkcji rozumienia zapytań kompilatorów C #, możesz pisać kod taki jak:

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; 

W powyższym wyrażeniu zapytania letsłowo kluczowe pozwala na przekazanie wartości do operacji where i uporządkowanie bez powielania wywołań animalName.Length.

Jaki jest równoważny zestaw wywołań metod rozszerzenia LINQ, który osiąga to, co robi słowo kluczowe „let”?

LBushkin
źródło
11
Do Twojej wiadomości, specyfikacja C # 3.0 wyjaśnia każdą regułę tłumaczenia rozumienia zapytań z bardzo szczegółowymi szczegółami.
Eric Lippert,
17
a dla tych, którzy uważają, że specyfikacja jest ciężka, obejmuje ją także C # in Depth Jona Skeeta ;-p
Marc Gravell
Specyfikacje językowe w języku C # to dokumenty Word do pobrania, których treść nie jest indeksowana przez wyszukiwarki i nie można ich połączyć ani przeglądać online. Byłoby bardzo pomocne, gdyby specyfikacje były dostępne online.
Olivier Jacot-Descombes

Odpowiedzi:

250

Niech nie ma własnej operacji; to piggy-backs z Select. Możesz to zobaczyć, jeśli użyjesz „reflektora”, aby rozdzielić istniejącą bibliotekę DLL.

będzie to coś w rodzaju:

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
źródło
4
Woah, nie wiedziałem, że możesz automatycznie kapsułkować używając nowego operatora w ten sposób.
David Pfeffer
19
Możesz również użyć małego przycisku „lambda” w okienku wyników LinqPad, aby zobaczyć wygenerowany kod, jeśli zaczniesz od Queryable. Innymi słowy, jeśli zmienisz swój pierwszy wiersz na var names = new string [] {"Dog", ...} .AsQueryable (); następnie uruchom wszystko w LinqPad, kliknij mały przycisk lambda, zobaczysz wygenerowany kod praktycznie identyczny z odpowiedzią Marca.
Reb.Cabin
3
Musiałem użyć .Dump()metody rozszerzenia w LinqPad, aby zobaczyć wynikową lambda.
justanotherdev,
88

Jest to dobry artykuł tutaj

Zasadniczo lettworzy anonimową krotkę. Jest to równoważne z:

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
źródło
Cytuję powyższy artykułit seems prudent to recommend against using the let keyword in cases where you do not need to transform a variable
JB. Z Moniką.
Cytuję dalej:This could be considered a micro-optimisation
Monsignor
7

W System.Interactive istnieje również metoda rozszerzenia .Let, ale jej celem jest wprowadzenie wyrażenia lambda, które będzie oceniane jako „w linii” w płynnym wyrażeniu. Na przykład, rozważ (powiedzmy w LinqPad) następujące wyrażenie, które tworzy nowe liczby losowe za każdym razem, gdy jest wykonywane:

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

Aby zobaczyć, że nowe losowe próbki pojawiają się za każdym razem, rozważ następujące kwestie

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

który tworzy pary, w których lewy i prawy są różne. Aby utworzyć pary, w których lewa i prawa są zawsze takie same, wykonaj następujące czynności:

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

Gdybyśmy mogli bezpośrednio wywoływać wyrażenia lambda, moglibyśmy pisać

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

Ale nie możemy wywoływać wyrażeń lambda tak, jakby były metodami.

Reb.Cabin
źródło
1

o kodzie równoważnym słowu kluczowemu „let” w połączonych wywołaniach metod rozszerzenia LINQ

powyższy komentarz nie jest już ważny

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

produkuje

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)

więc wiele letjest teraz zoptymalizowanych

Nieważne
źródło