Pemanggilan method biasa selesai sebelum baris berikutnya dieksekusi. Iterator method berbeda: pemanggilan MenuVegetarian() pada bab sebelumnya tidak menjalankan satu baris pun dari badan method, melainkan hanya mengembalikan objek urutan yang siap dibaca. Karakter ini disebut lazy iteration atau eksekusi tunda. Sifat ini menjelaskan perilaku yield return yang mungkin terasa aneh pada pertama kali dilihat, sekaligus menjadi fondasi eksekusi LINQ yang dibahas di section tersendiri.
Bagaimana Eksekusi Ditunda #
Compiler mengubah badan iterator method menjadi state machine yang dikendalikan MoveNext():
- Saat method dipanggil, hanya state machine yang dibuat; tidak ada pernyataan di badan yang berjalan.
- Setiap
MoveNext()menjalankan badan hinggayield returnberikutnya, lalu berhenti di sana. - Setelah item terakhir dikirim, sisa badan dieksekusi dan
MoveNext()mengembalikanfalse.
Karena state machine baru dibuat untuk setiap pemanggilan GetEnumerator(), dua enumerasi atas variabel yang sama menjalankan badan method dua kali penuh.
Contoh Jejak Eksekusi #
Method berikut mencetak jejak [iterator] agar urutan eksekusi terlihat:
public static IEnumerable<string> SumberCatatan()
{
Console.WriteLine(" [iterator] mulai menghasilkan item");
yield return "Catatan A";
Console.WriteLine(" [iterator] lanjut ke item berikutnya");
yield return "Catatan B";
Console.WriteLine(" [iterator] selesai");
}
IEnumerable<string> catatan = SumberCatatan();
Console.WriteLine("Method sudah dipanggil, iterator belum berjalan");
Console.WriteLine("Enumerasi pertama:");
foreach (string isi in catatan)
{
Console.WriteLine(isi);
}
Console.WriteLine("Enumerasi kedua:");
foreach (string isi in catatan)
{
Console.WriteLine(isi);
}Output yang Dihasilkan:
Method sudah dipanggil, iterator belum berjalan
Enumerasi pertama:
[iterator] mulai menghasilkan item
Catatan A
[iterator] lanjut ke item berikutnya
Catatan B
[iterator] selesai
Enumerasi kedua:
[iterator] mulai menghasilkan item
Catatan A
[iterator] lanjut ke item berikutnya
Catatan B
[iterator] selesaiMari kita bedah urutan keluarannya:
Method sudah dipanggil, iterator belum berjalan: baris ini tercetak sebelum jejak[iterator]mana pun, membuktikan pemanggilanSumberCatatan()belum mengeksekusi badan method.- Jejak pertama muncul setelah label
Enumerasi pertama: pernyataanConsole.WriteLinepertama di badan iterator baru berjalan ketikaforeachmemanggilMoveNext()untuk pertama kali. - Selang-seling
Catatan Adan jejak: setiapyield returnmenyerahkan kendali kembali ke konsumen; badan method bergerak satu potongan perMoveNext(). Enumerasi keduamengulang jejak yang sama: enumerasi kedua membuat state machine baru, sehingga seluruh badan method, termasuk jejak awal, berjalan ulang dari nol.
Konsekuensi desainnya nyata: iterator yang membaca sumber berubah, misalnya data stok terkini, menghasilkan data terbaru pada setiap enumerasi. Sifat ini menguntungkan, asalkan kita menyadarinya.