Un eveniment este de fapt un delegate mai special. Comportamentul este destul de asemanator a unui eveniment in interiorul clasei unde este declarat, dar in afara acestei clase singurul lucru care se poate face este subscribed si unsubscribed. In momentul cand cineva face (un)subscribed, putem sa executam actiuni specifice prin definirea explicita a actiunilor de "Add" si "Remove"( per event).
Chiar daca pentru un eveniment putem sa specificam nivelul de acces( public, protected, private, internal), asta nu inseamna ca oricine poate sa faca raise la eveniment. Doar clasa care contine evenimentul poate sa faca raise la eveniment.
In mod normal evenimentele nu trebuie sa execute o actiune pe obiectul care contine acest eveniment, ele trebuie doar sa notifice abonatii ca ceva s-a intamplat. Din aceasta cauza evenimentele trebuie privite ca ceva optional. Aruncarea evenimentului este ceva optional, iar orice exceptie aparuta in momentul respectiv nu ar trebuii sa afecteze logica blocului de cod care face raise la eveniment.
Din punctul meu de vedere evenimentele trebuie folosite cand vrem sa notificam 1 sau mai multi abonati ca ceva s-a intamplat. In cazul in care vrem ca abonatul sa faca o actiune care poate afecta clasa care a facut raise la eveniment, atunci este necesar sa folosim delegates.
Pentru mai multe informatii despre events si delegates:
Chiar daca pentru un eveniment putem sa specificam nivelul de acces( public, protected, private, internal), asta nu inseamna ca oricine poate sa faca raise la eveniment. Doar clasa care contine evenimentul poate sa faca raise la eveniment.
In mod normal evenimentele nu trebuie sa execute o actiune pe obiectul care contine acest eveniment, ele trebuie doar sa notifice abonatii ca ceva s-a intamplat. Din aceasta cauza evenimentele trebuie privite ca ceva optional. Aruncarea evenimentului este ceva optional, iar orice exceptie aparuta in momentul respectiv nu ar trebuii sa afecteze logica blocului de cod care face raise la eveniment.
Din punctul meu de vedere evenimentele trebuie folosite cand vrem sa notificam 1 sau mai multi abonati ca ceva s-a intamplat. In cazul in care vrem ca abonatul sa faca o actiune care poate afecta clasa care a facut raise la eveniment, atunci este necesar sa folosim delegates.
Pentru mai multe informatii despre events si delegates:
- Delegates - http://msdn.microsoft.com/en-us/library/ms173171%28v=vs.80%29.aspx
- Events - http://msdn.microsoft.com/en-us/library/ms535863%28v=vs.85%29.aspx
Legat de
ReplyDelete"Aruncarea evenimentului este ceva optional, iar orice exceptie aparuta in momentul respectiv nu ar trebuii sa afecteze logica blocului de cod care face raise la eveniment"
adevarat, raise la evenimente ar trebui facut doar la sfarsit, dupa ce actiunea a fost terminata cu success..
Insa daca un event handler arunca totusi o exceptie (desi nu prea e frumos asta :) ), cine implementeaza componenta respectiva nu prea are ce face decat sa o arunce mai departe..
Dar mecanismul prin care facem raise trebuie sa fie a.i. in momentul in care apare o eroare pe Invoke, logica clasei noastre sa nu fie afectata.
ReplyDeleteDepinde cred si de tipul de clasa. Daca suntem pe UI, atunci e normal sa aruncam exceptia mai departe, dar daca este o clasa ce proceseaza date( ceva pe back-end) atunci logica din spate nu trebuie sa fie afectata daca un eveniment arunca o exceptie. Nu cred ca este problema acestei clase daca un client arunca o exceptie. Poate sa scrie intr-un log, sau sa arunce exceptia mai departe( printr-un alt thread de exemplu), dar logica clasei de baza trebuie sa ramana la fel.
Adevarul e ca n-am prea intalnit folosite Events in .NET decat folosite in contexte de UI (WinForms, WPF, ASP.NET), dat fiind ca observer pattern e folosit mai ales in contextul asta..
ReplyDeleteAltfel, ai dreptate:
http://c2.com/cgi/wiki?ObserversShouldNeverThrowExceptions
Eu am folosit evenimente si in back-end. Spre exemplu daca am un fir de executie care proceseaza date, arunc evenimente periodic ca sa anunt progresul (in procente). La eveniment se poate abona controllerul care, la randul lui modifica un progress bar pe ui.
ReplyDeleteIn cazul exemplului de mai sus, intr-adevar nu as vrea ca o exceptie aruncata de controller sa intrerupa procesarea datelor din back-end. Pt asta eu am folosit pana acum un try-catch. (astept pareri pro-contra daca aveti)
try
{
OnProgressChanged(new ProgressChangedEventArgs(i));
}
catch {}
In felul asta eu anunt progresul, dar nu opresc procesarea datelor daca vr-unul dintre abonati arunca exceptie. (Anuntarea progresului e ceva optional, nu e vital pt procesarea datelor.)
Obs! Pe catch, exceptia poate fi scrisa intr-un log file.
E o chestei de preferinte - eu prefer principiul "fail fast" (http://martinfowler.com/ieeeSoftware/failFast.pdf ; http://c2.com/cgi/wiki?FailFast) - daca apare o exceptie in aplicatie, din orice sursa, care nu stiu/nu pot sa o tratez, prefer sa o loghez, sa afisez un mesaj user-friendly la user, eventual sa incerc sa salvez starea aplicatiei (si/sau sa eliberez resursele alocate, eventual) dupa care sa las thread-ul curent sa moara.
ReplyDeleteChiar daca e logata, o exceptie unhandled poate insemna doua lucruri:
- un bug in aplicatie (si deseori asta poate inseman ca aplicatia e intr-un state "unexpected" in care daca executia se continua rezultatele pot fi cel putin eronate)
- o exceptie dupa care executia nu mai poate continua oricum in conditii normale (gen stack overflow, out of memory etc.)
Normal, sunt si dezavantaje la abordarea asta: se prea poate ca o exceptie "expected" sa apara si sa nu fie prinsa, desi ar fi trebuit, mai mult efort trebuie alocat pentru a trata corespunzator toate tipurile de erori etc.
E oarecum similara cu dilema legate de checked exceptions din Java vs. C#..