CodeGym /Corsi /C# SELF /Pattern «Publisher-Subscriber» e eventi in C# (

Pattern «Publisher-Subscriber» e eventi in C# ( event)

C# SELF
Livello 52 , Lezione 0
Disponibile

1. Introduzione

Lavorando con i programmi, inevitabilmente ti trovi in situazioni in cui una parte deve «informare» le altre che è successo qualcosa di importante. L'esempio classico — l'utente ha cliccato con il mouse, e quell'evento richiede gestione. Nella vita di tutti i giorni viviamo già nel mondo degli eventi: la kettlera in cucina fischia — hai sentito il segnale e hai spento i fornelli. Il caffè è caduto sulla tastiera — il cuore ha fatto un balzo — e sei corso a salvare il laptop. La programmazione segue le stesse regole.

Evento — è un meccanismo che permette a un oggetto-sorgente (publisher) di avvisare altri oggetti (subscriber) di cambiamenti o azioni avvenute. È una specie di «ho segnalato — chi ha ascoltato risponde».

In C# gli eventi sono una costruzione speciale basata su un tipo delegate. Il delegate definisce la signature della callback — cosa e come verrà chiamato nei subscriber. La dichiarazione di un evento si fa con la keyword event, e il delegate — con la keyword delegate.

Perché servono gli eventi?

  • Coupling debole: il publisher non sa nulla sui subscriber — invia solo il segnale.
  • Flessibilità dell'architettura: puoi aggiungere e rimuovere handler dinamicamente senza cambiare il codice del publisher.
  • Scalabilità: aggiungi un nuovo subscriber — e inizia subito a ricevere notifiche.

Il classico pattern «Publisher-Subscriber»

Immaginiamo di avere una classe «Allarme antincendio» (publisher) e una classe «Persona nell'edificio» (subscriber). Quando l'allarme suona, invia il segnale a tutti contemporaneamente — non importa quante persone ci siano nell'edificio o dove si trovino. Questo è il pattern «Publisher-Subscriber» (o Observer).

Il publisher non sa quanti e quali subscriber esistono — semplicemente notifica, e gli altri si iscrivono alle notifiche o le ignorano.

Come funziona in C#?

  • Publisher: definisce l'evento (event), fornisce sottoscrizione/cancellazione.
  • Subscriber: si iscrive all'evento e implementa un handler (il metodo-handler verrà chiamato quando l'evento accade).

2. Eventi in pratica: primo esempio

Passando dalla teoria al codice, descriviamo un modello minimale. Supponiamo di avere un'app console dove un oggetto-timer «ticca» ogni secondo, e diversi handler reagiscono (per esempio, stampano «tic» sulla console o contano il numero di tic).

Passo 1. Definiamo il delegate e l'evento


public class SimpleTimer
{
    // Dichiariamo il delegate per l'evento
    public delegate void TickEventHandler(object sender, EventArgs e);

    // Evento basato sul delegate
    public event TickEventHandler Tick;

    public void Start(int count)
    {
        for (int i = 0; i < count; i++)
        {
            System.Threading.Thread.Sleep(1000); // simulazione del tic!
            OnTick(); // scatenare l'evento!
        }
    }

    protected virtual void OnTick()
    {
        // Chiamiamo l'evento, se ci sono subscriber (Tick != null)
        Tick?.Invoke(this, EventArgs.Empty);
    }
}

Cosa succede qui?

  • È definito il delegate TickEventHandler con la signature classica object sender, EventArgs e.
  • L'evento Tick — è il punto di sottoscrizione per gli handler.
  • Il metodo Start simula il «tic» e chiama periodicamente OnTick.
  • In OnTick l'evento viene chiamato in modo sicuro: Tick?.Invoke(..., EventArgs.Empty).

Passo 2. Iscrizione all'evento


class Program
{
    static void Main()
    {
        var timer = new SimpleTimer();

        // Ci iscriviamo all'evento Tick
        timer.Tick += Timer_Tick;

        timer.Start(3);

        // Possiamo cancellare l'iscrizione, se serve
        timer.Tick -= Timer_Tick;
    }

    static void Timer_Tick(object sender, EventArgs e)
    {
        Console.WriteLine("Tic!");
    }
}

Creiamo il timer, ci iscriviamo con l'operatore +=, poi ad ogni tic viene chiamato l'handler. La cancellazione usa l'operatore -=.

3. Dettagli utili

Perché gli eventi sono preferibili rispetto a chiamate dirette?

Se SimpleTimer dentro OnTick scrivesse direttamente sulla console, la classe sarebbe rigidamente accoppiata a quell'azione concreta. Gli eventi «liberano» il codice: il timer non sa cosa faranno i subscriber — lanciare un razzo, loggare su file o inviare una e-mail.

Importante differenza tra eventi e delegate

  • Delegate — è un «puntatore» a un metodo, mentre evento — è un delegate con restrizioni di accesso.
  • I subscriber possono solo iscriversi/annullare l'iscrizione; chiamare l'evento dall'esterno non è permesso — solo il publisher può farlo.
  • Per dichiarare un evento, aggiungi il modificatore event al tipo delegate — il compilatore garantirà il modello di accesso corretto.

Schema sintetico del funzionamento di un evento in C#


+------------------+          +------------------------------+
|                  |          |                              |
|    Publisher     | <------> |         Subscriber           |
| (Publisher/Event)|          |      (Subscriber/Handler)    |
|                  |          |                              |
+------------------+          +------------------------------+
         | 1) dichiara evento       | 2) si iscrive ad esso
         | 3) lo invoca             | 4) implementa l'handler

Quando usare gli eventi?

  • Quando devi notificare un numero indefinito di listener su qualcosa che è successo.
  • Quando non vuoi legare la logica delle azioni dentro la classe sorgente.
  • UI, interazioni asincrone, notifiche di sistema — tutto ruota intorno agli eventi.

Brevi caratteristiche dell'implementazione degli eventi in C#

  • Un evento non può essere invocato «dall'esterno»: solo il codice del publisher ha il diritto di chiamare Invoke.
  • Sottoscrizione/cancellazione: operatori +=/-=; possono esserci più handler.
  • Un evento è fondamentalmente una lista di delegate: quando l'evento scatta, vengono chiamati tutti gli handler nell'ordine di sottoscrizione.
  • Delegate raccomandati: usa EventHandler e EventHandler<TEventArgs> per compatibilità con l'ecosistema .NET.

4. Errori tipici dei principianti

Dimenticare di controllare se ci sono subscriber: Tick != null. Meglio usare la chiamata sicura: Tick?.Invoke(...).

Iscriversi a un evento e non cancellare l'iscrizione quando l'handler non è più necessario. Questo può tenere oggetti in memoria e causare memory leak.

Provare a «invocare» un evento da una classe esterna — il compilatore non lo permetterà. Non puoi scrivere qualcosa come game.GameOver(), se si tratta di un evento e non di un metodo.

Non rispettare la signature del delegate. Per gli eventi usa gli standard EventHandler o EventHandler<TEventArgs> — così il codice resta compatibile con le altre librerie .NET.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION