Skip to main content

What to use? Event or delegate?

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:


Comments

  1. Legat de
    "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..

    ReplyDelete
  2. 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.
    Depinde 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.

    ReplyDelete
  3. 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..
    Altfel, ai dreptate:
    http://c2.com/cgi/wiki?ObserversShouldNeverThrowExceptions

    ReplyDelete
  4. 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.

    In 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.

    ReplyDelete
  5. 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.

    Chiar 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#..

    ReplyDelete

Post a Comment

Popular posts from this blog

Windows Docker Containers can make WIN32 API calls, use COM and ASP.NET WebForms

After the last post , I received two interesting questions related to Docker and Windows. People were interested if we do Win32 API calls from a Docker container and if there is support for COM. WIN32 Support To test calls to WIN32 API, let’s try to populate SYSTEM_INFO class. [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public uint dwOemId; public uint dwPageSize; public uint lpMinimumApplicationAddress; public uint lpMaximumApplicationAddress; public uint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public uint dwProcessorLevel; public uint dwProcessorRevision; } ... [DllImport("kernel32")] static extern void GetSystemInfo(ref SYSTEM_INFO pSI); ... SYSTEM_INFO pSI = new SYSTEM_INFO(...

ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded

Today blog post will be started with the following error when running DB tests on the CI machine: threw exception: System.InvalidOperationException: The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer' registered in the application config file for the ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information. at System.Data.Entity.Infrastructure.DependencyResolution.ProviderServicesFactory.GetInstance(String providerTypeName, String providerInvariantName) This error happened only on the Continuous Integration machine. On the devs machines, everything has fine. The classic problem – on my machine it’s working. The CI has the following configuration: TeamCity .NET 4.51 EF 6.0.2 VS2013 It see...

Navigating Cloud Strategy after Azure Central US Region Outage

 Looking back, July 19, 2024, was challenging for customers using Microsoft Azure or Windows machines. Two major outages affected customers using CrowdStrike Falcon or Microsoft Azure computation resources in the Central US. These two outages affected many people and put many businesses on pause for a few hours or even days. The overlap of these two issues was a nightmare for travellers. In addition to blue screens in the airport terminals, they could not get additional information from the airport website, airline personnel, or the support line because they were affected by the outage in the Central US region or the CrowdStrike outage.   But what happened in reality? A faulty CrowdStrike update affected Windows computers globally, from airports and healthcare to small businesses, affecting over 8.5m computers. Even if the Falson Sensor software defect was identified and a fix deployed shortly after, the recovery took longer. In parallel with CrowdStrike, Microsoft provi...