event și EventHandler în C# (.NET)

Un eveniment anunță abonații când se întâmplă ceva; din afara clasei te poți doar abona (+=) sau dezabona (-=).

Sintaxă C#
public event EventHandler<int>? SoldScazut;   SoldScazut?.Invoke(this, sold);

Ce face event și EventHandler

Un event este un delegat protejat: doar clasa care îl declară îl poate declanșa, iar codul din afară poate doar să adauge sau să scoată handler-e cu += și -=. Așa un obiect anunță schimbări (un buton apăsat, un sold scăzut) fără să știe cine ascultă.

Convenția .NET este tipul EventHandler sau EventHandler<TEventArgs>: primul parametru este obiectul care trimite evenimentul (sender), al doilea datele lui. Declanșarea se face cu Eveniment?.Invoke(this, date), ca să nu apară eroare când nu există abonați.

Abonamentul ține obiectul abonat în viață cât timp trăiește sursa. Dezabonează-te cu -= când abonatul nu mai are nevoie de eveniment, altfel apar scurgeri de memorie.

Exemple

Exemplul 1: Eveniment cu date: sold scăzut

var cont = new Cont(500);
cont.SoldScazut += (sender, sold) => Console.WriteLine($"Atenție: au rămas {sold} lei");

cont.Retrage(200);
cont.Retrage(250);
Console.WriteLine($"Sold final: {cont.Sold}");

class Cont(int sold)
{
    public int Sold { get; private set; } = sold;
    public event EventHandler<int>? SoldScazut;

    public void Retrage(int suma)
    {
        Sold -= suma;
        if (Sold < 100)
            SoldScazut?.Invoke(this, Sold);
    }
}

Rezultat

Atenție: au rămas 50 lei
Sold final: 50

Exemplul 2: Mai mulți abonați și dezabonare

var senzor = new Senzor();
EventHandler afisaj = (s, e) => Console.WriteLine("Afișaj actualizat");
senzor.Schimbat += afisaj;
senzor.Schimbat += (s, e) => Console.WriteLine("Jurnal scris");

senzor.Masoara();
senzor.Schimbat -= afisaj;
senzor.Masoara();

class Senzor
{
    public event EventHandler? Schimbat;
    public void Masoara() => Schimbat?.Invoke(this, EventArgs.Empty);
}

Rezultat

Afișaj actualizat
Jurnal scris
Jurnal scris

Greșeli frecvente

  • cont.SoldScazut = handler; sau cont.SoldScazut(...) din afara clasei nu se compilează: evenimentul permite doar += și -=.
  • Declanșarea fără ?. aruncă NullReferenceException când nu există abonați.
  • O excepție într-un handler oprește și handler-ele de după el.

Comenzi înrudite

  • delegate: Un delegat este un tip care ține o referință spre o metodă cu o anumită semnătură; poate ține mai multe metode (multicast).
  • Func, Action și Predicate: Tipurile de delegat gata făcute din .NET: Func returnează o valoare, Action nu returnează nimic, Predicate returnează bool.
  • Expresii lambda (=>): O funcție scurtă, fără nume, scrisă pe loc: parametri => rezultat. Se folosește în LINQ, la evenimente și oriunde se cere un delegat.