Event field-like pada bab-bab sebelumnya menyembunyikan backing field dan accessors di dalam compiler. Ketika kita perlu mengawasi setiap perubahan langganan, misalnya mencatat jumlah subscriber alarm stok, deklarasikan event dengan accessor add dan remove. Sejak saat itu, backing field menjadi tanggung jawab kita sendiri.

Ketika accessor ditulis manual, compiler berhenti membuat backing field dan pengamanan otomatis. Kebebasan ini dibayar dengan disiplin: field harus kita deklarasikan sendiri, dan setiap accessor wajib menyentuhnya. Pola yang sama dipakai framework untuk memvalidasi handler, mencatat statistik langganan, atau menambah logika thread-safety pada event yang diakses lintas thread.

Accessor add dan remove #

csharp
class AlarmStok
{
    private EventHandler _stokKritis;

    public int JumlahSubscriber => _stokKritis?.GetInvocationList().Length ?? 0;

    public event EventHandler StokKritis
    {
        add
        {
            _stokKritis += value;
            Console.WriteLine($"Subscriber baru ditambahkan (total {JumlahSubscriber})");
        }
        remove
        {
            _stokKritis -= value;
            Console.WriteLine($"Subscriber dilepas (sisa {JumlahSubscriber})");
        }
    }

    public void NaikkanAlarm()
    {
        _stokKritis?.Invoke(this, EventArgs.Empty);
    }
}

AlarmStok alarm = new();
EventHandler catat = (pengirim, e) => Console.WriteLine("Laporan: stok kritis tercatat");
EventHandler pesan = (pengirim, e) => Console.WriteLine("Pesan: minta restock segera");

alarm.StokKritis += catat;
alarm.StokKritis += pesan;
alarm.NaikkanAlarm();
alarm.StokKritis -= pesan;

Output yang Dihasilkan:

text
Subscriber baru ditambahkan (total 1)
Subscriber baru ditambahkan (total 2)
Laporan: stok kritis tercatat
Pesan: minta restock segera
Subscriber dilepas (sisa 1)

Bedah Kode Baris per Baris #

Mari kita telaah kode di atas:

  1. Baris 3: Field privat _stokKritis menyimpan daftar handler; compiler tidak lagi membuatnya untuk kita.
  2. Baris 5: Properti JumlahSubscriber membaca panjang invocation list, sehingga kode lain dapat memeriksa beban alarm tanpa menyentuh field.
  3. Baris 7-18: Accessor add dan remove menerima handler sebagai keyword value. Setiap operasi dicatat, dan jumlah subscriber terbaru ikut tercetak.
  4. Baris 25-26: Dua lambda pertama dipegang variabel catat dan pesan agar -= pada baris terakhir dapat menghapus handler yang tepat.
  5. Baris 29: NaikkanAlarm memicu event dengan EventArgs.Empty karena alarm tidak membawa data tambahan.
  6. Baris 30: Melepas pesan juga melewati accessor remove, terbukti dari baris output Subscriber dilepas (sisa 1).

Perhatikan bahwa lambda catat dan pesan dipegang variabel terlebih dahulu. Dua lambda dengan isi yang sama tetap merupakan dua objek handler yang berbeda, sehingga -= terhadap lambda baru yang ditulis langsung tidak akan menghapus pendaftaran lama.