Area di riferimento
- Developing applications that use system types and collections
- Control interactions between .NET Framework application components by using events and delegates
- EventArgs class
- EventHandler delegates
Events
Un evento permette a un tipo di notificare ad altri oggetti che si è verificato qualcosa di speciale. Il classico esempio è l'evento offerto dalla classe Button chiamato Click. Quando un pulsante viene cliccato, uno o più oggetti nell'applicazione riceveranno una notifica in modo tale che possano eseguire determinate azioni.
I tipi possono offrire questa funzionalità definendo opporuni membri di tipo event. Un tipo event mantiene una lista di metodi registrati i quali saranno eseguiti al momento in cui l'evento viene sollevato. Il modello a eventi del CLR si basa sull'utilizzo dei delegates che abbiamo visto essere un modo type-safe per invocare un metodo di callback.
Definire un tipo che espone un evento
Vediamo in che modo possiamo costruire un tipo che espone un evento. Nell'esempio didattico viene realizzata una classe Magazzino che espone l'evento MagazzinoAggiornato il quale verrà sollevato solo quando la quantità di oggetti nelle scorte è cambiata. Coloro che gestiranno l'evento inoltre potranno sapere quanti oggetti sono stati aggiunti o tolti a seguito di questa operazione di aggiornamento.
// Definisco un tipo utilizzato per memorizzare informazioni aggiuntive ad un evento.
// Il suffisso EventArgs è una convenzione.
class MagazzinoAggiornatoEventArgs : EventArgs
{
private int _differenzaOggetti;
public int DifferenzaOggetti
{
get
{
return _differenzaOggetti;
}
}
public MagazzinoAggiornatoEventArgs(int differenzaOggetti)
{
_differenzaOggetti = differenzaOggetti;
}
}
// Tipo che espone l'evento MagazzinoAggiornato
class Magazzino
{
private int _numeroOggetti = 0;
// Definisco l'evento MagazzinoAggiornato. I Suffissi e i nomi dei parametri sono una convenzione.
public delegate void MagazzinoAggiornatoEventHandler(object sender, MagazzinoAggiornatoEventArgs e);
public event MagazzinoAggiornatoEventHandler MagazzinoAggiornato;
public int NumeroOggetti
{
get
{
return _numeroOggetti;
}
set
{
if (value != _numeroOggetti)
{
// Calcolo il numero di oggetti aggiunti/tolti
int differenzaOggetti = value - _numeroOggetti;
// Aggiorno il numero di oggetti nel magazzino
_numeroOggetti = value;
// Sollevo l'evento MagazzinoAggiornato passando le informazioni addizionali
OnMagazzinoAggiornato(new MagazzinoAggiornatoEventArgs(differenzaOggetti));
}
}
}
// Metodo responsabile per sollevare l'evento e di conseguenza notificare tutti gli oggetti
// che lo hanno sottoscritto. Il prefisso On è una convenzione.
protected virtual void OnMagazzinoAggiornato(MagazzinoAggiornatoEventArgs e)
{
// Assegno un oggetto delegate a una variabile temporanea prima di invocare il delegate
// per evitare condizioni di contesa in caso di utilizzo concorrente dell'oggetto
MagazzinoAggiornatoEventHandler handler = MagazzinoAggiornato;
// Sollevo l'evento se esiste almeno un gestore
if (handler != null)
{
handler(this, e);
}
}
}
class Program
{
static void Main(string[] args)
{
Magazzino m = new Magazzino();
m.MagazzinoAggiornato += new Magazzino.MagazzinoAggiornatoEventHandler(m_MagazzinoAggiornato);
m.NumeroOggetti = 10;
m.NumeroOggetti = 10;
m.NumeroOggetti = 25;
m.NumeroOggetti = 5;
Console.ReadKey();
}
// Gestore dell'evento di aggiornamento del Magazzino
static void m_MagazzinoAggiornato(object sender, MagazzinoAggiornatoEventArgs e)
{
Magazzino m = sender as Magazzino; // Recupero l'oggetto Magazzino
if ( m != null)
{
Console.WriteLine("Magazzino aggiornato: Numero Oggetti = {0} - Oggetti Aggiunti/Tolti = {1}",
m.NumeroOggetti, e.DifferenzaOggetti);
}
}
}
I passi da seguire quindi sono i seguenti:
- Definire, se necessario, un tipo che conterrà informazioni addizionali sull'evento generato.
- Definire un membro di tipo event che si basa su un particolare delegate EventHandler
- Definire un metodo responsabile per sollevare l'evento.
- Chiamare il metodo precedente dove si desidera generare l'evento specificando eventuali informazioni aggiuntive.
Il metodo nel punto 3 è bene definirlo protected virtual in modo tale da permettere alle eventuali classi derivate di fare l'override e quindi gestire diversamente l'evento se necessario.