Tidak semua operator menunda pekerjaan. Operator agregasi dan materialisasi mengeksekusi kueri seketika pada baris tempat operator itu dipanggil, lalu mengembalikan nilai atau wadah yang isinya sudah final. Bab ini menunjukkan kapan snapshot terbentuk dan kapan kueri tetap mengikuti sumber.

Operator yang Langsung Mengeksekusi #

Contoh berikut membandingkan salinan ToList dengan kueri deferred pada sumber yang sama, lalu mengubah sumber:

csharp
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("Roti Bakar", "Makanan", 12000, 25),
    new Produk("Susu UHT", "Minuman", 20000, 35)
};

var kueriMinuman = produk.Where(p => p.Kategori == "Minuman");
List<Produk> salinan = produk.Where(p => p.Kategori == "Minuman").ToList();
int totalStok = produk.Sum(p => p.Stok);
string makananPertama = produk.First(p => p.Kategori == "Makanan").Nama;

produk.Add(new Produk("Es Teh", "Minuman", 8000, 50));

Console.WriteLine($"Salinan: {salinan.Count} produk");
Console.WriteLine($"Total stok: {totalStok}");
Console.WriteLine($"Makanan pertama: {makananPertama}");
Console.WriteLine($"Kueri Count() setelah tambah: {kueriMinuman.Count()}");
Console.WriteLine($"Salinan setelah tambah: {salinan.Count}");

Output yang Dihasilkan:

text
Salinan: 3 produk
Total stok: 160
Makanan pertama: Roti Bakar
Kueri Count() setelah tambah: 4
Salinan setelah tambah: 3

Mari kita bedah baris demi baris:

  1. kueriMinuman: kueri deferred biasa; baris ini belum menghitung apa pun.
  2. produk.Where(...).ToList(): salinan dibuat sebelum produk baru ditambahkan, sehingga isinya terkunci pada saat panggilan.
  3. produk.Sum(...) dan produk.First(...): kedua nilai dihitung seketika dari empat produk yang ada.
  4. produk.Add(...): sumber berubah setelah seluruh panggilan di atas selesai.
  5. kueriMinuman.Count(): kueri deferred dihitung ulang pada baris ini, sehingga Es Teh ikut terhitung; salinan ToList tidak ikut berubah.

Daftar Operator Immediate #

Operator berikut selalu mengeksekusi kueri pada baris pemanggilannya:

  • Agregasi: Count, Sum, Average, Min, Max, Aggregate
  • Elemen: First, FirstOrDefault, Single, Last, ElementAt
  • Materialisasi: ToList, ToArray, ToDictionary, ToLookup

Pola di atas menjadi alasan ToList sering dipanggil di akhir kueri: hasilnya menjadi snapshot yang stabil untuk ditampilkan, disimpan, atau dikirim ke bagian aplikasi lain.

Dua perilaku pada daftar itu perlu dibedakan. Operator agregasi dan elemen hanya mengembalikan satu nilai, sedangkan operator materialisasi menyalin seluruh elemen ke wadah baru. Selain itu, Count() tanpa predikat tetap menelusuri sumber IEnumerable<T>; hanya List<T>.Count dan Array.Length yang langsung membaca ukuran tanpa iterasi. Di sisi lain, First pada contoh berhenti segera setelah elemen cocok ditemukan, sehingga tidak seluruh sumber ditelusuri. Kombinasi perilaku-perilaku ini menentukan seberapa sering sumber data sesungguhnya diakses oleh aplikasi.