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

Concetti

Eventi e delegati

Altre risorse

Gestione e generazione di eventi