Generazione di un evento
Se si desidera che la propria classe generi un evento, è necessario fornire i tre elementi seguenti:
Una classe che fornisce i dati dell'evento.
Un delegato dell'evento.
La classe che genera l'evento.
Definizione di un classe per fornire i dati dell'evento
Per convenzione, in .NET Framework quando viene generato un evento i dati dell'evento vengono passati ai relativi gestori. I dati dell'evento vengono forniti dalla classe System.EventArgs o da una classe derivata da essa.
Spesso un evento non dispone di dati personalizzati. Il fatto che l'evento sia stato generato fornisce tutte le informazioni necessarie per i gestori dell'evento. In questo caso, l'evento può passare un oggetto EventArgs ai propri gestori. La classe EventArgs dispone di un solo membro, Empty, che non è ereditato da System.Object. Essa può essere utilizzata per creare un'istanza di una nuova classe EventArgs.
Se un evento dispone di dati personalizzati, può passare un'istanza di una classe derivata da EventArgs ai gestori dell'evento. A seconda dei dati precisi passati dall'evento ai gestori, potrebbe essere possibile utilizzare una classe di dati dell'evento esistente in .NET Framework. Se, ad esempio, il gestore dell'evento consente di annullare l'azione associata all'evento, è possibile utilizzare la classe CancelEventArgs.
Quando è necessario fornire dati personalizzati ai gestori e non è disponibile una classe esistente, è possibile definire una classe di dati dell'evento. Tale classe deve derivare da System.EventArgs. Per convenzione, questa classe è denominata EventNameEventArgs. Nell'esempio seguente viene illustrata tale classe di dati dell'evento personalizzata. Viene definita una classe denominata AlarmEventArgs, che fornisce due elementi di dati ai gestori dell'evento: la proprietà Time di sola lettura che indica quando l'allarme è stato disattivato e la proprietà Snooze che indica se l'allarme deve venire disattivato di nuovo dopo un intervallo definito o se gli allarmi futuri devono essere annullati.
Public Class AlarmEventArgs : Inherits EventArgs
Private alarmTime As Date
Private snoozeOn As Boolean = True
Public Sub New(time As Date)
Me.alarmTime = time
End Sub
Public ReadOnly Property Time As Date
Get
Return Me.alarmTime
End Get
End Property
Public Property Snooze As Boolean
Get
Return Me.snoozeOn
End Get
Set
Me.snoozeOn = value
End Set
End Property
End Class
public class AlarmEventArgs : EventArgs
{
private DateTime alarmTime;
private bool snoozeOn = true;
public AlarmEventArgs(DateTime time)
{
this.alarmTime = time;
}
public DateTime Time
{
get { return this.alarmTime; }
}
public bool Snooze
{
get { return this.snoozeOn; }
set { this.snoozeOn = value; }
}
}
public ref class AlarmEventArgs : public EventArgs
{
private:
System::DateTime^ alarmTime;
bool snoozeOn;
public:
AlarmEventArgs(System::DateTime^ time)
{
this->alarmTime = time;
this->snoozeOn = true;
}
property DateTime^ Time
{
System::DateTime^ get()
{ return this->alarmTime; }
}
property bool Snooze
{
bool get()
{ return this->snoozeOn; }
void set(bool snooze)
{ this->snoozeOn = snooze; }
}
};
Definizione di un delegato per l'evento
Un delegato dell'evento viene utilizzato per definire la firma dell'evento. Un delegato dell'evento specifico corrisponde in genere a una classe di dati dell'evento specifica. Per convenzione, gli eventi in .NET Framework dispongono della firma EventName(sender, e), dove sender è un oggetto Object che fornisce un riferimento alla classe o alla struttura che ha generato l'evento ed e è un oggetto EventArgs o un oggetto derivato da EventArgs che fornisce i dati dell'evento. La definizione del delegato ha in genere il formato EventNameHandler(sender, e).
Se si utilizza una classe di dati dell'evento già definita nella libreria di classi .NET Framework o in una libreria di terze parti, è probabile che in tale libreria sia già definito un delegato dell'evento corrispondente. Il delegato EventHandler può ad esempio essere utilizzato con la classe EventArgs. Analogamente, il delegato CancelEventHandler può essere utilizzato con la classe CancelEventArgs.
Se si definisce una classe di dati dell'evento personalizzata, è possibile definire anche un delegato personalizzato per definire la firma dell'evento oppure è possibile utilizzare il delegato Action<T1, T2> generico.
Nell'esempio seguente viene definito un delegato dell'evento denominato AlarmEventHandler.
Public Delegate Sub AlarmEventHandler(sender As Object, e As AlarmEventArgs)
public delegate void AlarmEventHandler(object sender, AlarmEventArgs e);
public delegate void AlarmEventHandler(System::Object^ sender, AlarmEventArgs^ e);
Definizione di un classe per generare l'evento
La classe che genera l'evento deve fornire la dichiarazione di evento e definire un metodo che genera l'evento. Deve inoltre fornire la logica per generare l'evento in un metodo o una proprietà della classe.
È possibile definire un membro dell'evento nella classe utilizzando la parola chiave event in C# o l'istruzione Event in Visual Basic. Quando il compilatore trova una dichiarazione di evento nella classe, crea un membro privato, ad esempio:
private EventNameHandler eh = null;
Il compilatore crea inoltre i due metodi pubblici add_EventName e remove_EventName. Tali metodi sono hook di evento che consentono di unire o dissociare delegati dal delegato di evento eh. Tali dettagli sono invisibili al programmatore.
Nota
In linguaggi diversi da C# e Visual Basic 2005, è possibile che il compilatore non generi automaticamente il codice corrispondente a un membro evento e che occorra quindi definire gli hook di evento e il campo delegato privato in modo esplicito.
Nell'esempio seguente viene dichiarato un evento denominato AlarmEvent. L'esempio è tratto da quello relativo a una classe denominata Alarm di cui viene di seguito illustrato il codice sorgente completo. Si noti che l'evento dispone della firma del delegato AlarmEventHandler.
Event AlarmEvent As AlarmEventHandler
public event AlarmEventHandler AlarmEvent;
public:
event AlarmEventHandler^ AlarmEvent;
Dopo aver definito l'implementazione dell'evento, è necessario stabilire il momento in cui generare l'evento. È possibile generare l'evento chiamando il metodo protetto OnEventName nella classe che definisce l'evento o in una classe derivata. Il metodo OnEventNamegenera quindi l'evento.
Nota
Il metodo protetto OnEventNameconsente inoltre alle classi derivate di eseguire l'override dell'evento senza dover associare un delegato.Perché l'evento sia ricevuto da delegati registrati, occorre che una classe derivata chiami sempre il metodo OnEventNamedella classe di base.
Nell'esempio seguente viene definito il metodo OnAlarmEvent, responsabile della generazione dell'evento AlarmEvent.
Protected Sub OnAlarmEvent(e As AlarmEventArgs)
RaiseEvent AlarmEvent(Me, e)
End Sub
protected void OnAlarmEvent(AlarmEventArgs e)
{
AlarmEvent(this, e);
}
protected:
void OnAlarmEvent(AlarmEventArgs^ e)
{
AlarmEvent(this, e);
}
Nell'esempio seguente viene definito un metodo denominato Set che contiene la logica per generare l'evento chiamando il metodo OnAlarmEvent. Se le ore e i minuti dell'ora impostata per l'attivazione dell'allarme equivalgono alle ore e ai minuti dell'ora corrente, il metodo Set crea un'istanza di un oggetto AlarmEventArgs e fornisce all'oggetto l'ora in cui l'allarme è stato disattivato. Dopo l'esecuzione dei gestori dell'evento, viene controllato il valore della proprietà Snooze. Se Snooze è false, non devono essere generati ulteriori eventi di allarme, pertanto il metodo Set può terminare. Se Snooze è true, l'ora di disattivazione dell'allarme viene incrementata del valore della proprietà Interval.
Public Sub [Set]()
Do
System.Threading.Thread.Sleep(2000)
Dim currentTime As DateTime = Date.Now
' Test whether it is time for the alarm to go off.
If currentTime.Hour = alarmTime.Hour And _
currentTime.Minute = AlarmTime.Minute Then
Dim args As New AlarmEventArgs(currentTime)
OnAlarmEvent(args)
If args.Snooze = False Then
Exit Sub
Else
Me.alarmTime = Me.alarmTime.AddMinutes(Me.interval)
End If
End If
Loop
End Sub
public void Set()
{
while (true) {
System.Threading.Thread.Sleep(2000);
DateTime currentTime = DateTime.Now;
// Test whether it is time for the alarm to go off.
if (currentTime.Hour == alarmTime.Hour &&
currentTime.Minute == alarmTime.Minute)
{
AlarmEventArgs args = new AlarmEventArgs(currentTime);
OnAlarmEvent(args);
if (! args.Snooze)
return;
else
this.alarmTime = this.alarmTime.AddMinutes(this.interval);
}
}
}
void Set()
{
do {
Thread::Sleep(2000);
System::DateTime^ currentTime = DateTime::Now;
// Test whether it's time for the alarm to go off.
if (currentTime->Hour == alarmTime->Hour && currentTime->Minute == alarmTime->Minute)
{
AlarmEventArgs^ args = gcnew AlarmEventArgs(currentTime);
OnAlarmEvent(args);
if (args->Snooze == false)
return;
else
this->alarmTime = this->alarmTime->AddMinutes(this->interval);
}
} while (true);
}
Nell'esempio seguente è incluso tutto il codice sorgente per la classe Alarm.
Public Class Alarm
Private alarmTime As Date
Private interval As Integer = 10
Event AlarmEvent As AlarmEventHandler
Public Sub New(time As Date)
Me.New(time, 10)
End Sub
Public Sub New(time As Date, interval As Integer)
Me.alarmTime = time
Me.interval = interval
End Sub
Public Sub [Set]()
Do
System.Threading.Thread.Sleep(2000)
Dim currentTime As DateTime = Date.Now
' Test whether it is time for the alarm to go off.
If currentTime.Hour = alarmTime.Hour And _
currentTime.Minute = AlarmTime.Minute Then
Dim args As New AlarmEventArgs(currentTime)
OnAlarmEvent(args)
If args.Snooze = False Then
Exit Sub
Else
Me.alarmTime = Me.alarmTime.AddMinutes(Me.interval)
End If
End If
Loop
End Sub
Protected Sub OnAlarmEvent(e As AlarmEventArgs)
RaiseEvent AlarmEvent(Me, e)
End Sub
End Class
public class Alarm
{
private DateTime alarmTime;
private int interval = 10;
public event AlarmEventHandler AlarmEvent;
public Alarm(DateTime time) : this(time, 10)
{
}
public Alarm(DateTime time, int interval)
{
this.alarmTime = time;
this.interval = interval;
}
public void Set()
{
while (true) {
System.Threading.Thread.Sleep(2000);
DateTime currentTime = DateTime.Now;
// Test whether it is time for the alarm to go off.
if (currentTime.Hour == alarmTime.Hour &&
currentTime.Minute == alarmTime.Minute)
{
AlarmEventArgs args = new AlarmEventArgs(currentTime);
OnAlarmEvent(args);
if (! args.Snooze)
return;
else
this.alarmTime = this.alarmTime.AddMinutes(this.interval);
}
}
}
protected void OnAlarmEvent(AlarmEventArgs e)
{
AlarmEvent(this, e);
}
}
public ref class Alarm
{
private:
System::DateTime^ alarmTime;
int interval;
public:
event AlarmEventHandler^ AlarmEvent;
Alarm(System::DateTime^ time) : alarmTime(time), interval(10) { };
Alarm(System::DateTime^ time, int interval) : alarmTime(time), interval(interval) {};
void Set()
{
do {
Thread::Sleep(2000);
System::DateTime^ currentTime = DateTime::Now;
// Test whether it's time for the alarm to go off.
if (currentTime->Hour == alarmTime->Hour && currentTime->Minute == alarmTime->Minute)
{
AlarmEventArgs^ args = gcnew AlarmEventArgs(currentTime);
OnAlarmEvent(args);
if (args->Snooze == false)
return;
else
this->alarmTime = this->alarmTime->AddMinutes(this->interval);
}
} while (true);
}
protected:
void OnAlarmEvent(AlarmEventArgs^ e)
{
AlarmEvent(this, e);
}
};
Vedere anche
Attività
Procedura: generare e utilizzare eventi
Procedura: implementare eventi nella classe