Deklarasi kueri LINQ tidak menjalankan apa pun. Kueri hanya menyimpan resep penyaringan, dan eksekusinya ditunda sampai hasilnya benar-benar diminta — perilaku ini disebut deferred execution. Bukti paling jelas datang dari mengubah sumber data setelah kueri dibuat.
Kueri Sebagai Resep #
Contoh berikut membuat kueri, menambahkan satu produk, lalu mengiterasi kueri yang sama dua kali:
record Produk(string Nama, string Kategori, int Harga, int Stok);
List<Produk> produk = new()
{
new Produk("Kopi Gayo", "Minuman", 25000, 40),
new Produk("Teh Melati", "Minuman", 15000, 60),
new Produk("Susu UHT", "Minuman", 20000, 35)
};
var mahal = produk.Where(p => p.Harga > 15000);
Console.WriteLine("Sebelum tambah:");
foreach (var p in mahal)
{
Console.WriteLine(p.Nama);
}
produk.Add(new Produk("Kopi Toraja", "Minuman", 30000, 10));
Console.WriteLine("Setelah tambah:");
foreach (var p in mahal)
{
Console.WriteLine(p.Nama);
}Output yang Dihasilkan:
Sebelum tambah:
Kopi Gayo
Susu UHT
Setelah tambah:
Kopi Gayo
Susu UHT
Kopi TorajaMari kita bedah baris demi baris:
var mahal = produk.Where(...): baris ini hanya membungkus sumber dan predikat menjadi satu object kueri; belum ada elemen yang diperiksa.foreachpertama: iterasi memicu eksekusi kueri; dua produk lolos syarat harga.produk.Add(...): produk baru ditambahkan setelah kueri dibuat, tepat di antara dua iterasi.foreachkedua: kueri dieksekusi ulang atas kondisi sumber saat ini, sehinggaKopi Torajaikut muncul.- kesimpulan: hasil kueri mengikuti keadaan sumber pada saat iterasi, bukan pada saat deklarasi.
Mekanisme di Balik Layar #
Mekanisme di balik perilaku ini adalah iterator: Where mengembalikan object yang menarik elemen dari sumber satu per satu hanya ketika foreach memintanya. Kueri tidak memegang salinan data; object kueri hanya menyimpan alamat sumber beserta predikat yang akan diterapkan. Konsekuensinya, satu kueri yang diiterasi dua kali menjalankan penyaringan dua kali; bab LINQ Performance mengukur dampaknya.
Sifat ini juga membuat kueri dapat disusun dari potongan kecil: variabel mahal di atas bisa menjadi sumber bagi kueri lain, misalnya mahal.OrderBy(p => p.Nama), tanpa mengeksekusi kueri pendahulunya lebih dulu. Seluruh rantai baru berjalan ketika hasil akhirnya diiterasi.
Operator yang justru langsung mengeksekusi kueri saat dipanggil dibahas pada bab Immediate Execution.