Aplikasi kasir menyimpan daftar item, aplikasi antrean menyimpan urutan pelanggan, dan keduanya sering dikirim ke method yang sama. Kebutuhan itu memunculkan pertanyaan desain: bagaimana sebuah method menerima List<T>, HashSet<T>, maupun koleksi lain tanpa membuat salinan untuk setiap tipe? Jawaban .NET adalah sekumpulan interface standar di namespace System.Collections.Generic. Interface tersebut menjadi kontrak yang dipenuhi hampir semua koleksi, dan menjadi bahasa bersama yang kita pakai sepanjang section ini.

Hierarki Interface Koleksi #

Interface koleksi disusun dari yang paling longgar ke paling ketat. Setiap interface menambah beberapa janji di atas induknya:

InterfaceJanji utamaContoh member
IEnumerable<T>Urutan item yang bisa dibacaGetEnumerator()
ICollection<T>Bisa dihitung, ditambah, dihapusCount, Add, Remove, Clear
IList<T>Akses berdasarkan indeksthis[int], Insert, RemoveAt
IDictionary<TKey, TValue>Pasangan kunci-nilaithis[TKey], Keys, Values
IReadOnlyList<T>Akses indeks tanpa izin menulisthis[int], Count

List<T> memenuhi IList<T>, ICollection<T>, dan IEnumerable<T> sekaligus, sehingga sebuah List<T> bisa dipandang sebagai tipe kontrak mana pun yang lebih longgar. Dua interface IReadOnlyList<T> dan IReadOnlyDictionary<TKey, TValue> berada di jalur terpisah: keduanya menjanjikan akses baca tanpa memberi member penulisan.

Deklarasi Lewat Interface #

Ketika variabel dideklarasikan dengan tipe interface, program hanya boleh memanggil member yang dijanjikan interface tersebut, meskipun objek aslinya lebih kaya:

csharp
IList<string> agendaRapat = new List<string> { "Sprint review", "Retrospektif" };
agendaRapat.Insert(0, "Rundown pagi");
agendaRapat.RemoveAt(1);

ICollection<int> nomorAntrean = new HashSet<int>();
nomorAntrean.Add(101);
nomorAntrean.Add(102);

IDictionary<string, int> stokBarang = new Dictionary<string, int>();
stokBarang["Kopi Robusta"] = 24;

Console.WriteLine($"Agenda pertama: {agendaRapat[0]}");
Console.WriteLine($"Jumlah agenda: {agendaRapat.Count}");
Console.WriteLine($"Jumlah nomor antrean: {nomorAntrean.Count}");
Console.WriteLine($"Stok kopi robusta: {stokBarang["Kopi Robusta"]} kg");

Output yang Dihasilkan:

text
Agenda pertama: Rundown pagi
Jumlah agenda: 2
Jumlah nomor antrean: 2
Stok kopi robusta: 24 kg

Mari kita bedah baris demi baris:

  1. IList<string> agendaRapat: objek List<string> dipandang sebagai kontrak berindeks, sehingga Insert dan RemoveAt tersedia.
  2. ICollection<int> nomorAntrean = new HashSet<int>(): variabel bertipe kontrak tambah-hapus item, meskipun implementasinya HashSet<T>.
  3. stokBarang["Kopi Robusta"] = 24;: indexer dari IDictionary<TKey, TValue> berlaku sama seperti pada Dictionary<TKey, TValue> langsung.
  4. Tiga baris Console.WriteLine terakhir: semua nilai dibaca lewat member kontrak; tidak ada satu pun cast yang diperlukan.

Bab berikutnya membedah kontrak paling dasar pada tabel di atas, yaitu IEnumerable<T> beserta pasangan IEnumerator<T>.