Modul laporan perlu tahu setiap kali item masuk ke keranjang, tetapi class keranjang tidak boleh bergantung pada modul tersebut. Cara naifnya adalah menjadikan delegate publik, tetapi cara itu bocor: siapa pun dapat menimpa seluruh daftar handler dengan satu penugasan, atau bahkan memicu delegate dari luar. Keyword event menambal kedua lubang itu sambil mempertahankan mekanisme delegate.
Event Membungkus Delegate dengan Aturan #
Deklarasi event membuat compiler menyimpan delegate di backing field privat. Dari luar class, satu-satunya operasi yang boleh dilakukan terhadap event adalah += untuk mendaftar handler dan -= untuk melepasnya. Pemicuan Invoke hanya sah dilakukan dari dalam class pemilik event.
Perbandingannya mudah diingat: delegate publik adalah pintu tanpa pagar, sedangkan event adalah pintu dengan daftar tamu. Subscriber hanya dapat menulis nama di daftar lewat += atau menghapus namanya lewat -=, sedangkan menimpa seluruh daftar dan memicu kejadian adalah hak pemilik class. Kontrak ini memaksa modul pendengar tumbuh melalui pendaftaran, bukan lewat penimpaan.
class KeranjangBelanja
{
public event Action<string> ItemDitambahkan;
public string Nama { get; }
public KeranjangBelanja(string nama) => Nama = nama;
public void Tambah(string item)
{
ItemDitambahkan?.Invoke(item);
}
}
KeranjangBelanja keranjang = new("Belanja mingguan");
keranjang.ItemDitambahkan += item =>
Console.WriteLine($"Ditambahkan ke {keranjang.Nama}: {item}");
keranjang.Tambah("Telur 1 kg");
keranjang.Tambah("Kopi sachet");Output yang Dihasilkan:
Ditambahkan ke Belanja mingguan: Telur 1 kg
Ditambahkan ke Belanja mingguan: Kopi sachetBedah Kode Baris per Baris #
Mari kita telaah kode di atas:
- Baris 3: Event field-like
ItemDitambahkanbertipeAction<string>; compiler membuat backing delegate privat beserta pasangan accessor add/remove default. - Baris 9-12: Method
Tambahadalah satu-satunya jalur pemicuan. Runtime tidak akan mengeksekusi handler apa pun sebelumTambahdijalankan. - Baris 11: Operator
?.memanggilInvokehanya bila ada subscriber, sehingga event tanpa handler tidak melemparNullReferenceException. - Baris 15-16: Lambda didaftarkan sebagai handler lewat
+=; operasi ini satu-satunya yang sah dilakukan dari luar class. - Baris 18-19: Setiap panggilan
Tambahmemicu event, dan handler berjalan seketika dengan nama item sebagai argumennya.
Lambda di baris pendaftaran menangkap variabel keranjang, sehingga pesan output memuat nama keranjang yang sebenarnya. Apabila nanti dibutuhkan modul pendengar tambahan, kita cukup menambah baris += baru; class KeranjangBelanja tidak berubah sama sekali, dan urutan handler mengikuti urutan pendaftaran.