C#: delegate e event, creare un evento personalizzato
Un breve esempio di come in c# vengano gestiti gli eventi. Il concetto alla base della loro gestione è il seguente: esiste una classe autore, che consente alla classi sottoscrittori di registrarsi ad un determinato evento. Nel momento in cui la classe autore segnala l’evento, tutti i sottoscrittori ne ricevono comunicazione.
Nell’esempio sottostante sono definite due classi:
Shuttle
: rappresenta uno shuttle in viaggio verso la luna. Dichiara un evento di nomeInviaComunicazione
, attraverso il quale invia messaggi alla Torre di controllo.TorreDiControllo
: Rappresenta la torre di controllo, che ascolta le comunicazioni di tutti gli Shuttles.
Un ipotetico main deve fare le seguenti cose:
- Creare la torre di controllo
- Creare tutti gli Shuttle
- Registrare la torre di controllo al sistema di comunicazione di tutti gli shuttles creati
- Far partire gli shuttles
Sotto forma di codice:
TorreDiControllo t = new TorreDiControllo(); Shuttle apollo1 = new Shuttle("Apollo 1"); Shuttle apollo13 = new Shuttle("Apollo 13"); t.InizializzaComunicazione(apollo1); t.InizializzaComunicazione(apollo13); apollo1.Partenza(); apollo13.Partenza(); |
Vediamo in dettaglio la classe più semplice, ovvero TorreDiControllo
. Nel metodo
public void InizializzaComunicazione(Shuttle shuttle) { shuttle.InviaComunicazione += new Shuttle.ComunicazioneEventHandler(OnComunicazione); } |
non viene fatto altro che registrarsi all’evento InviaComunicazione
della classe Shuttle, specificando che tale evento viene gestito dal metodo OnComunicazione
:
/// <summary> /// Ogni volta che una classe Shuttle genera un evento InviaComunicazione /// verrà chiamato questo metodo. /// </summary> /// <param name="sender"></param> /// <param name="a"></param> void OnComunicazione(object sender, ComunicazioneEventArgs a) { Console.WriteLine("Torre di controllo: " + a.Comunicazione); } |
Quando si definisce un evento personalizzato si definisce in genere anche l’informazione che viene comunicata dall’autore a tutti i sottoscrittori, in questo esempio dallo Shuttle alla torre di controllo. In questo esempio è una semplice stringa di testo:
public class ComunicazioneEventArgs : EventArgs { public ComunicazioneEventArgs(string s) { _comunicazione = s; } private string _comunicazione; public string Comunicazione { get { return comunicazione; } set { comunicazione = value; } } } |
Vediamo la classe Shuttle
, che deve dichiarare e scatenare all’occorrenza l’evento InviaComunicazione
. La creazione di evento è possibile tramite l’utilizzo di delegati (delegate): in questo esempio ne verrà dichiarato e utilizzato uno di nome ComunicazioneEventHandler
.
Il metodo OnInviaComunicazione
è un metodo privato usato per generare l’evento InviaComunicazione
. Non fa altro che sfruttare le caratteristiche dei delegati invocando il delegato ComunicazioneEventHandler
, lanciando così tutti i metodi dei sottoscrittori registrati.
Ecco la classe Shuttle
completa:
/// <summary> /// Rappresenta uno shuttle in viaggio verso la luna /// </summary> public class Shuttle { /// <summary> /// Il delegato, che punterà a tutti i metodi sottoscrittori. /// Tutto questo è permesso grazie a lui. /// </summary> /// <param name="sender"></param> /// <param name="a"></param> public delegate void ComunicazioneEventHandler(object sender, ComunicazioneEventArgs a); /// <summary> /// La definizione dell'evento che lo shuttle sarà in grado di scatenare /// </summary> public event ComunicazioneEventHandler InviaComunicazione; /// <summary> /// Il nome dello Shuttle /// </summary> String nome; /// <summary> /// Costruisco lo Shuttle dandogli un nome /// </summary> /// <param name="name">Il nome dello Shuttle</param> public Shuttle(String name) { this.nome = name; } /// <summary> /// Lo Shuttle parte verso la luna /// </summary> public void Partenza() { /* * Siamo in viaggio... * */ if (!nome.Equals("Apollo 13") ) { OnInviaComunicazione( new ComunicazioneEventArgs("Qui " + nome + ": siamo arrivati sulla luna!")); } else { OnInviaComunicazione( new ComunicazioneEventArgs("Qui " + nome + ": Huston, abbiamo un problema...")); } } /// <summary> /// Tutti i metodi della classe Shuttle che vogliono inviare una comunicazione /// lo faranno chiamando questo metodo. /// "protected" significa che è visibile solo dalle classi che estendono /// la classe Shuttle. /// "virtual" significa che la classe che estende Shuttle può fare un override /// di questo metodo /// </summary> /// <param name="e">Classe che rappresenta l'unità di informazione /// trasportata dall'evento</param> protected virtual void OnInviaComunicazione(ComunicazioneEventArgs e) { // Viene creata una copia dell'evento, e viene usata quella, per evitare // la situazione in cui la torre di controllo decide di non comunicare più // con lo shuttle tra il controllo sul null e il fire dell'evento, altrimenti // potrebbe avverarsi questo scenario: // 1 - La torre di controllo si sintonizza in attesa delle comunicazioni da questo shuttle // 2 - if (handler != null) // 3 - La torre di controllo spegne la radio // 4 - handler(this, e); // 5 - BUM ComunicazioneEventHandler handler = InviaComunicazione; // Se nessuna torre di controllo è in ascolto allora l'evento è null if (handler != null) { // L'evento è un delegato, quindi eseguendo il delegato eseguiamo tutti i metodi // ad esso registrati. handler(this, e); } } } |
Il progetto Esempio Eventi contiene il codice sopra descritto.
Leave a Reply