Event memiliki siklus hidup yang berulang di setiap fitur yang memakainya: dideklarasikan, diisi subscriber, dipicu, lalu ditinggalkan subscriber. Urutan ini terlihat sepele, tetapi sebagian besar bug event berasal dari tahap yang terlewat, misalnya handler terpanggil dua kali atau tidak terpanggil sama sekali. Bab ini menelusuri keempat tahap pada satu alur pembayaran kasir.

Keempat tahap tidak selalu berurutan di dalam satu method: pada aplikasi UI, langganan biasanya terjadi saat halaman dibuka dan pelepasan saat halaman ditutup. Urutan logikanya tetap sama, dan bab Event Memory Leaks akan menunjukkan akibat nyata bila tahap keempat terlewat.

Empat Tahap Siklus Event #

  1. Deklarasi: publisher menuliskan event; backing field bernilai null karena belum ada subscriber.
  2. Langganan: subscriber mendaftar handler lewat +=.
  3. Pemicuan: publisher memanggil Invoke; seluruh handler berjalan berurutan.
  4. Pelepasan: subscriber melepas handler lewat -=, dan pemicuan berikutnya tidak lagi menjangkau handler tersebut.
csharp
class MesinKasir
{
    public event Action<string> TransaksiSelesai;

    public void Bayar(string nomor)
    {
        Console.WriteLine($"Pembayaran {nomor} diproses");
        TransaksiSelesai?.Invoke(nomor);
    }
}

MesinKasir kasir = new();
Console.WriteLine("Tahap 1: event dideklarasikan, belum ada subscriber");

void CatatKuitansi(string nomor) => Console.WriteLine($"Kuitansi {nomor} dicetak");
kasir.TransaksiSelesai += CatatKuitansi;
Console.WriteLine("Tahap 2: subscriber mendaftar lewat +=");

kasir.Bayar("TRX-501");
Console.WriteLine("Tahap 3: publisher memicu event, handler dieksekusi");

kasir.TransaksiSelesai -= CatatKuitansi;
Console.WriteLine("Tahap 4: subscriber melepas diri lewat -=");
kasir.Bayar("TRX-502");

Output yang Dihasilkan:

text
Tahap 1: event dideklarasikan, belum ada subscriber
Tahap 2: subscriber mendaftar lewat +=
Pembayaran TRX-501 diproses
Kuitansi TRX-501 dicetak
Tahap 3: publisher memicu event, handler dieksekusi
Tahap 4: subscriber melepas diri lewat -=
Pembayaran TRX-502 diproses

Bedah Kode Baris per Baris #

Mari kita telaah kode di atas:

  1. Baris 3: Deklarasi event menandai tahap pertama. Sebelum ada +=, pemanggilan Invoke akan melewati ?. tanpa efek apa pun.
  2. Baris 11-12: Local function CatatKuitansi mendaftar sebagai handler; inilah tahap langganan.
  3. Baris 16: Bayar("TRX-501") mencetak status pembayaran, lalu handler menjalankan pencetakan kuitansi. Urutan output menunjukkan pemicuan terjadi di dalam method Bayar.
  4. Baris 19-21: Operator -= menyelesaikan tahap pelepasan. Pemanggilan Bayar("TRX-502") hanya mencetak baris pembayaran tanpa kuitansi, sebab daftar handler sudah kosong.

Output menegaskan dua hal: handler hanya berjalan setelah terdaftar, terlihat dari kuitansi TRX-501 yang tercetak di antara dua baris Tahap, dan pemicuan setelah pelepasan tidak menjangkau handler apa pun, terlihat dari TRX-502 tanpa kuitansi. Ilustrasi empat tahap pada alur kecil ini mudah dipindahkan ke skenario aplikasi nyata.