Closure menangkap variabel, bukan nilai, dan aturan itu berdampak serius di dalam loop. Satu variabel iterasi yang ditangkap oleh banyak lambda tetap menjadi satu variabel yang sama, sehingga semua lambda melihat nilai terakhirnya. Bab ini membuktikannya pada daftar tugas pencetak, lalu menunjukkan perbaikan standarnya.

Aturan mainnya sederhana: closure menangkap variabel, dan scope loop for hanya memiliki satu variabel i untuk seluruh iterasi. Semua lambda yang lahir di loop itu menunjuk variabel yang sama. Perbaikannya juga sederhana: buat variabel baru per iterasi, dan compiler membuat closure baru untuk variabel itu. Dua loop di bawah hanya berbeda pada satu hal, yaitu ada tidaknya variabel salinan.

Satu Variabel, Banyak Lambda #

csharp
List<Action> tanpaSalin = new();
for (int i = 1; i <= 3; i++)
{
    tanpaSalin.Add(() => Console.Write($"{i} "));
}
foreach (Action aksi in tanpaSalin)
{
    aksi();
}
Console.WriteLine();

List<Action> denganSalin = new();
for (int i = 1; i <= 3; i++)
{
    int nomor = i;
    denganSalin.Add(() => Console.Write($"{nomor} "));
}
foreach (Action aksi in denganSalin)
{
    aksi();
}
Console.WriteLine();

Output yang Dihasilkan:

text
4 4 4 
1 2 3 

Bedah Kode Baris per Baris #

Mari kita telaah kode di atas:

  1. Baris 1-5: Setiap iterasi menambahkan lambda yang menangkap variabel i. Karena i hidup di scope loop, ketiga lambda berbagi satu variabel yang sama.
  2. Baris 6-10: Loop selesai ketika i bernilai 4; nilai itulah yang terlihat ketika ketiga lambda akhirnya dijalankan, sehingga output pertama 4 4 4.
  3. Baris 12-17: Versi perbaikan mendeklarasikan int nomor = i di dalam iterasi. Variabel baru berarti closure baru untuk setiap lambda.
  4. Baris 18-22: Tiga closure memegang tiga variabel nomor yang berbeda, sehingga output kedua 1 2 3.

Bug “semua output sama” seperti ini sering muncul terlambat: kode berjalan tanpa error, tetapi hasilnya salah pada pemanggilan lambda yang tertunda. Menjalankan lambda langsung di dalam loop menyembunyikan masalah; menyimpannya untuk dieksekusi nanti memperlihatkannya.