book-openEventArgs

Un eveniment nu este doar o notificare de tipul „s-a întâmplat ceva". În practică, handler-ul care primește notificarea are nevoie de context pentru a putea acționa în mod util. Când o comandă își schimbă starea, handler-ul vrea să știe: care comandă, din ce stare, în ce stare nouă. Când o comandă este livrată, vrea să știe data livrării și, eventual, costul de transport.

Fără un mecanism de transmitere a datelor, singura alternativă ar fi ca handler-ul să interogheze el însuși obiectul sursă după primirea notificării. Aceasta introduce un cuplaj suplimentar și poate produce inconsistențe dacă starea obiectului s-a modificat între momentul declanșării evenimentului și momentul interogării.

Soluția standard în C# este transmiterea datelor direct prin eveniment, ca parte a notificării. Mecanismul pentru aceasta este clasa EventArgs și clasele derivate din ea.

Clasa EventArgs

EventArgs este o clasă definită în System, din care derivă toate clasele de date asociate evenimentelor. În sine nu conține proprietăți utile, ci există ca punct de extensie, un contract care spune: „orice clasă derivată din mine poate fi transmisă ca al doilea argument al unui handler de eveniment".

Există și o instanță specială, EventArgs.Empty, care se folosește când un eveniment nu transportă nicio dată suplimentară:

public event EventHandler DepozitInchis;

// Declansare fara date suplimentare
DepozitInchis?.Invoke(this, EventArgs.Empty);

Când evenimentul are date specifice, se creează o clasă derivată din EventArgs cu proprietățile relevante.

Crearea claselor EventArgs personalizate

Convenția de denumire este NumeEvenimentEventArgs (numele evenimentului urmat de sufixul EventArgs). Clasa nu conține logică: este un container de date transmis de la emițătorul evenimentului la handler-ele abonate.

// Date transmise la fiecare schimbare de stare a comenzii
class ComandaSchimbatStareEventArgs : EventArgs
{
    public Comanda Comanda { get; set; }
    public StareComanda StareVeche { get; set; }
    public StareComanda StareNoua { get; set; }
}

// Date transmise cand comanda ajunge in starea Livrata
class ComandaLivrataEventArgs : EventArgs
{
    public Comanda Comanda { get; set; }
    public DateTime DataLivrare { get; set; }
}

Fiecare clasă conține exact proprietățile relevante pentru contextul evenimentului respectiv. ComandaSchimbatStareEventArgs transmite atât starea veche, cât și starea nouă. Handler-ul nu trebuie să mai interogheze obiectul Comanda pentru a afla tranziția completă. ComandaLivrataEventArgs adaugă data livrării, informație care poate fi diferită de momentul apelului handler-ului dacă procesarea este asincronă.

Legătura dintre EventArgs și declararea evenimentului

Tipul EventArgs personalizat este specificat ca parametru generic al lui EventHandler<T>:

Fiecare eveniment are propriul tip EventArgs, ceea ce înseamnă că fiecare are o semnătură de handler distinctă. Compilatorul garantează că handler-ul abonat la ComandaSchimbatStare primește un ComandaSchimbatStareEventArgs, nu un ComandaLivrataEventArgs, astfel încât nu există risc de confuzie între tipuri la runtime.

Construirea obiectului EventArgs la declanșare

Clasa de date este instanțiată în metoda care declanșează evenimentul, cu toate proprietățile completate la momentul declanșării:

Obiectul EventArgs este creat o singură dată, cu toate datele disponibile la momentul declanșării, și este transmis tuturor handler-elor abonate. Handler-ele primesc o imagine consistentă a stării la momentul evenimentului - nu trebuie să interogheze obiectul sursă și nu există riscul ca starea să se fi modificat între timp.

Scrierea handler-elor

Handler-ele respectă semnătura (object sender, TEventArgs e) și accesează datele prin parametrul e:

Parametrul sender este convertit la tipul concret al sursei cu operatorul as. Dacă conversia eșuează (sursa nu este de tipul așteptat), as returnează null în loc să arunce excepție, ceea ce permite tratarea gracioasă a cazurilor neașteptate.

Abonarea și observarea completă

Piesele se asamblează într-un flux complet:

Fiecare apel la AvansezaStare declanșează evenimentul ComandaSchimbatStare. Când comanda ajunge în starea Livrata, se declanșează și ComandaLivrata. Toți abonații la fiecare eveniment primesc notificarea, cu datele complete ale tranzitiei, fără să fie nevoie să cunoască logica internă a clasei Depozit.

Dezabonarea și limitările lambda-urilor anonime

Dezabonarea cu -= necesită aceeași referință folosită la abonare. Metodele denumite pot fi dezabonate oricând:

Lambda-urile adăugate fără stocare într-o variabilă nu pot fi dezabonate ulterior, deoarece nu există nicio referință prin care să poată fi identificate. Dacă dezabonarea va fi necesară, lambda-ul trebuie stocat:

Last updated