Pernyataan foreach sudah kita pakai sejak section Dasar Pemrograman, tetapi foreach bukanlah fitur bawaan koleksi. foreach hanyalah singkatan yang ditulis compiler atas kontrak iterasi milik IEnumerable<T> dan IEnumerator<T>. Memahami kedua interface ini penting karena seluruh ekosistem .NET, mulai dari LINQ sampai serialisasi JSON, bergerak di atas kontrak yang sama.
Dua Interface yang Saling Melengkapi #
IEnumerable<T> mewakili “sesuatu yang bisa diiterasi” dan hanya punya satu member, yaitu method GetEnumerator() yang mengembalikan enumerator. IEnumerator<T> mewakili “satu sesi pembacaan” dengan tiga member utama:
MoveNext(): maju ke item berikutnya, mengembalikanfalsesaat item habis.Current: membaca item pada posisi sekarang.Dispose(): membersihkan sumber daya enumerator ketika iterasi selesai.
Satu objek bisa diiterasi berkali-kali karena setiap pemanggilan GetEnumerator() membuat enumerator baru, sementara satu enumerator hanya menyeberangi koleksi satu kali.
Iterasi Manual Tanpa foreach #
Contoh berikut melakukan persis pekerjaan yang biasanya compiler lakukan untuk foreach:
List<string> pesertaLomba = new List<string> { "Sari", "Dimas", "Lina" };
IEnumerable<string> sumberPeserta = pesertaLomba;
IEnumerator<string> enumerator = sumberPeserta.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
}
finally
{
enumerator.Dispose();
}Output yang Dihasilkan:
Sari
Dimas
LinaMari kita bedah baris demi baris:
IEnumerable<string> sumberPeserta = pesertaLomba;:List<T>dipandang lewat kontrak paling longgar; kode setelah baris ini tidak tahu objek aslinya adalahList<T>.sumberPeserta.GetEnumerator(): sesi pembacaan baru diminta; enumerator memulai posisi sebelum item pertama.while (enumerator.MoveNext()): setiap putaran maju satu item; method mengembalikantrueselama masih ada item, danfalseketika ketiga nama selesai dibaca.enumerator.Current: nama pada posisi sekarang dibaca dan dicetak; member ini hanya valid setelahMoveNext()mengembalikantrue.finally { enumerator.Dispose(); }: enumeratorList<T>mengimplementasikanIDisposable, sehingga pembersihan dijamin berjalan meskipun iterasi dihentikan di tengah jalan.
Bila kode manual di atas kita ganti dengan foreach (string nama in pesertaLomba), hasilnya identik: compiler menerjemahkan foreach menjadi bentuk GetEnumerator-while-Current-Dispose tersebut.
Kontrak dua interface ini yang kita isi pada bab Iterator Pattern: sebuah koleksi menyediakan enumerator, dan konsumen membaca isinya tanpa mengetahui struktur internal.