Property biasa mengekspos satu nilai per nama. Ketika class menyimpan kumpulan data, misalnya Keranjang yang memuat daftar barang, kita perlu cara membaca dan menulis item satu per satu dengan notasi yang sudah akrab: kurung siku. Indexer menyediakan notasi tersebut. Dengan indexer, pemanggil menulis keranjang[0] pada object class, sama nyamannya dengan mengakses elemen array.

Masalah: Akses Item Koleksi yang Ekspresif #

Class Keranjang menyimpan barang di dalam List<string> yang bersifat privat. Membuka list tersebut ke publik melanggar encapsulation karena kode luar dapat menambah, menghapus, atau mengganti isinya semaunya. Menyediakan method bernama AmbilItem(int index) dan GantiItem(int index, string barang) bisa dilakukan, tetapi notasi pemanggilannya panjang. Indexer memberi akses terkontrol sambil mempertahankan sintaks kurung siku yang ringkas.

Mekanisme Indexer #

Indexer dideklarasikan dengan kata kunci this karena anggota ini tidak memiliki nama sendiri:

  • Deklarasi public string this[int index] mendefinisikan indexer bertipe string yang menerima satu parameter int.
  • Aksesor get menerima parameter index dan mengembalikan item pada posisi tersebut; aksesor set mengganti item pada posisi itu dengan value.
  • Notasi pemanggilan sama seperti array: keranjang[0] memanggil aksesor get, dan keranjang[1] = "..." memanggil aksesor set.
  • Indexer dapat dideklarasikan lebih dari satu pada class yang sama selama daftar parameternya berbeda, misalnya versi int dan versi string.

Contoh: Indexer pada Class Keranjang #

csharp
public class Keranjang
{
    private List<string> barang = new List<string>
    {
        "Beras", "Minyak Goreng", "Gula"
    };

    public string this[int index]
    {
        get { return barang[index]; }
        set { barang[index] = value; }
    }

    public int JumlahItem
    {
        get { return barang.Count; }
    }
}

Keranjang keranjang = new Keranjang();
Console.WriteLine($"Item pertama: {keranjang[0]}");

keranjang[1] = "Minyak Kelapa";
for (int i = 0; i < keranjang.JumlahItem; i++)
{
    Console.WriteLine($"Item {i}: {keranjang[i]}");
}

try
{
    Console.WriteLine(keranjang[5]);
}
catch (ArgumentOutOfRangeException e)
{
    Console.WriteLine($"Index tidak valid: {e.Message}");
}

Output yang Dihasilkan:

text
Item pertama: Beras
Item 0: Beras
Item 1: Minyak Kelapa
Item 2: Gula
Index tidak valid: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')

Mari kita bedah baris demi baris:

  1. private List<string> barang = ...: koleksi asli tetap privat; satu-satunya akses dari luar adalah indexer dan property JumlahItem.
  2. public string this[int index]: deklarasi indexer; kata this menggantikan nama anggota, dan parameter index menjadi isi kurung siku saat pemanggilan.
  3. keranjang[0]: pembacaan memanggil aksesor get, yang meneruskan index ke barang[index] milik list.
  4. keranjang[1] = "Minyak Kelapa";: penugasan memanggil aksesor set dengan index bernilai 1 dan value berisi "Minyak Kelapa", sehingga item pada posisi itu terganti.
  5. keranjang[5] di dalam blok try: index 5 berada di luar isi list, aksesor get meneruskan permintaan ke list, dan list melempar ArgumentOutOfRangeException yang ditangkap blok catch.