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 nome InviaComunicazione, 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

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>