Bagaimana cara kerja pernyataan LINQ berikut?


160

Bagaimana cara kerja pernyataan LINQ berikut ?

Ini kode saya:

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0);
list.Add(8);
foreach (var i in even)
{
    Console.WriteLine(i);
}

Keluaran: 2, 4, 6, 8

Mengapa tidak 2, 4, 6?


102
Hasil ekspresi kueri adalah kueri, bukan eksekusi kueri.
Eric Lippert

6
Untuk informasi yang lebih sedikit, lihat jawaban yang diterima untuk pertanyaan ini .
Daniel

9
Tentunya Anda bisa memikirkan judul yang sebenarnya meringkas pertanyaan.
Matt Ball

2
Dugaan saya tentang downvotes (6 sekarang, bukan milik saya) adalah bahwa mereka menganggap judul pertanyaan terlalu umum untuk menjadi pertanyaan yang bagus. Tapi, melihat jumlah upvotes, dan menjadi pertanyaan teratas minggu ini di buletin, saya pikir Anda tidak perlu terlalu khawatir tentang hal itu.
Abel

Jawaban:


235

Outputnya adalah 2,4,6,8karena eksekusi yang ditangguhkan .

Permintaan sebenarnya dieksekusi ketika variabel kueri diulangi, bukan saat variabel kueri dibuat. Ini disebut eksekusi yang ditangguhkan.

- Suprotim Agarwal, "Eksekusi Kueri Ditunda vs Segera di LINQ"

Ada eksekusi lain yang disebut Eksekusi Kueri Segera , yang berguna untuk caching hasil pencarian. Dari Suprotim Agarwal lagi:

Untuk memaksa eksekusi segera dari sebuah query yang tidak menghasilkan nilai tunggal, Anda bisa memanggil metode ToList(), ToDictionary(), ToArray(), Count(), Average()atau Max()pada variabel query atau query. Ini disebut operator konversi yang memungkinkan Anda untuk membuat salinan / snapshot dari hasil dan akses sebanyak yang Anda inginkan, tanpa perlu menjalankan kembali kueri.

Jika Anda ingin hasilnya 2,4,6, gunakan .ToList():

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0).ToList();
list.Add(8);
foreach (var i in even)
 {
    Console.WriteLine(i);
 }

8
Hitung (), Maks (), Rta (), Jumlah () dan mungkin metode lain yang perlu mempertimbangkan seluruh daftar, juga menyebabkan evaluasi kueri.
Kennedy

1
Saya sering berpikir untuk memiliki, katakanlah, 'filteredList' sebagai variabel, daripada 'filterList ()' sebagai metode - idenya adalah, Anda mengulanginya setiap kali Anda ingin daftar difilter, daripada memanggil metode. Mungkin menjadi metode yang menarik, jika tidak biasa dan mungkin tidak sempurna, dalam melakukan sesuatu.
Katana314

4
@Sebastian - Selanjutnya @ komentar Kenned ini, .First(), .FirstOrDefault(), .Single()dan .SingleOrDefault()juga memicu evaluasi query.
Scotty.NET

4
mengherankan bagaimana Anda mendapatkan jawabannya dalam waktu kurang dari 30 detik: D
MC

2
@ MC Saya tidak tahu mengapa Anda menanyakan pertanyaan ini. Seluruh jawaban tidak diberikan sekaligus. Itu diedit beberapa kali.
Atish Dipongkor - MVP

11

Ini terjadi karena eksekusi yang ditangguhkan, yang berarti bahwa perhitungan ekspresi tidak dieksekusi sampai diperlukan di suatu tempat. Ini membuat kinerja lebih baik jika datanya terlalu besar.


3
Anda dapat menambahkannya, karena itu juga dapat berarti bahwa penghitungan mahal Anda dilakukan beberapa kali. Dalam kasus seperti itu Anda bahkan mungkin menderita kehilangan kinerja.
Meringis Putus Asa

0

Alasan untuk ini adalah eksekusi yang ditangguhkan dari ekspresi lambda Anda. Kueri dieksekusi ketika Anda mulai iterasi di loop foreach.


11
Secara teknis itu adalah eksekusi iterator yang ditangguhkan , bukan lambda .
D Stanley

0

Ketika Anda menggunakan IEnumerable <> yang diperoleh dari LINQ, hanya dibuat kelas Enumerator dan iterasi hanya dimulai ketika Anda menggunakannya dalam beberapa langkah.


-1

Anda mendapatkan hasil ini karena eksekusi yang ditangguhkan yang berarti hasil sebenarnya tidak dievaluasi sampai diakses pertama kali.

Untuk membuatnya lebih jelas, tambahkan saja 10 ke daftar di ujung snipet Anda dan kemudian cetak lagi Anda tidak akan mendapatkan 10 output

     var list = new List<int>{1,2,4,5,6};
    var even = list.Where(m => m%2 == 0).Tolist();
    list.Add(8);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }
//new*
    list.Add(10);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }

Apakah Anda benar-benar mencobanya? Saya mendapatkan 10output.
Mark Hurd

good catch @MarkHurd ya tidak ditambahkan .ToList (). diedit pos sekarang harus memberikan hasil yang diharapkan. Ekspektasi saya adalah ekspresi dievaluasi hanya ketika Anda menggunakan var untuk pertama kali tetapi sepertinya akan dievaluasi setiap kali
sandeep

Sekarang tidak akan berisi 8dalam output mana pun.
Mark Hurd
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.