Kode yang setara dengan kata kunci 'let' dalam panggilan metode ekstensi LINQ dirantai

192

Dengan menggunakan fitur kompilasi permintaan kompiler C #, Anda dapat menulis kode seperti:

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; 

Dalam ekspresi kueri di atas, letkata kunci memungkinkan nilai diteruskan ke tempat dan operasi orberby tanpa panggilan duplikat animalName.Length.

Apa set yang setara dengan panggilan metode ekstensi LINQ yang mencapai apa yang dilakukan kata kunci "biarkan" di sini?

LBushkin
sumber
11
FYI, spesifikasi C # 3.0 menjelaskan setiap aturan penerjemahan pemahaman kueri dalam detail yang luar biasa.
Eric Lippert
17
dan bagi mereka yang menemukan berat spek pergi, Jon Skeet C # di sampul Kedalaman juga ;-p
Marc Gravell
Spesifikasi Bahasa C # adalah dokumen Word yang dapat diunduh yang isinya tidak diindeks oleh mesin pencari dan tidak dapat ditautkan atau dijelajahi secara online. Akan sangat membantu jika spesifikasinya tersedia secara online.
Olivier Jacot-Descombes

Jawaban:

250

Biarkan tidak memiliki operasi sendiri; itu piggy-back off Select. Anda dapat melihat ini jika Anda menggunakan "reflektor" untuk memisahkan dll yang ada.

itu akan menjadi sesuatu seperti:

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
sumber
4
Woah, saya tidak tahu Anda bisa secara otomatis merangkum menggunakan operator baru seperti itu.
David Pfeffer
19
Anda juga dapat menggunakan tombol "lambda" kecil di panel hasil LinqPad untuk melihat kode yang dihasilkan jika Anda mulai dengan Queryable. Dengan kata lain, jika Anda mengubah baris pertama Anda menjadi var names = new string [] {"Dog", ...} .AsQueryable (); kemudian jalankan semuanya di LinqPad, klik tombol lambda kecil, Anda akan melihat kode yang dihasilkan hampir identik dengan jawaban Marc.
Reb.Cabin
3
Saya perlu menggunakan .Dump()metode ekstensi di LinqPad untuk melihat lambda yang dihasilkan.
justanotherdev
88

Ada artikel bagus di sini

Pada dasarnya letmenciptakan tupel anonim. Ini setara dengan:

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
sumber
Saya mengutip artikel di atasit seems prudent to recommend against using the let keyword in cases where you do not need to transform a variable
JB. Dengan Monica.
Saya kutip lebih lanjut:This could be considered a micro-optimisation
Monsignor
7

Ada juga metode ekstensi .Let di System.Interactive, tetapi tujuannya adalah untuk memperkenalkan ekspresi lambda untuk dievaluasi 'in-line' dalam ekspresi yang lancar. Misalnya, pertimbangkan (dalam LinqPad, katakanlah) ekspresi berikut yang membuat angka acak baru setiap kali dieksekusi:

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

Untuk melihat bahwa sampel acak baru muncul setiap waktu, pertimbangkan yang berikut ini

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

yang menghasilkan pasangan di mana kiri dan kanan berbeda. Untuk menghasilkan pasangan di mana kiri dan kanan selalu sama, lakukan sesuatu seperti berikut:

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

Jika kita bisa memanggil ekspresi lambda secara langsung, kita bisa menulis

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

Tapi kita tidak bisa memunculkan ekspresi lambda seolah-olah itu metode.

Reb.Cabin
sumber
1

tentang Kode yang setara dengan kata kunci 'biarkan' dalam panggilan metode ekstensi LINQ berantai

komentar di atas tidak lagi valid

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

menghasilkan

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)

jadi banyak letyang dioptimalkan sekarang

Tidak penting
sumber