引發事件

如果您希望類別引發事件,您必須提供下列三個項目:

  • 提供事件資料的類別。

  • 事件委派。

  • 引發事件的類別。

定義類別來提供事件資料

在 .NET Framework 中,依照慣例,當引發事件時,這個事件會將事件資料傳遞至其事件處理常式。 事件資料由 System.EventArgs 類別或其衍生類別所提供。

事件通常沒有自訂資料,已引發事件的這項事實會提供事件處理常式所需的一切資訊。 在此情況下,事件可以將 EventArgs 物件傳遞至其處理常式。 EventArgs 類別只有一個成員,就是 Empty,這個成員不是繼承自 System.Object。 這個成員可以用於執行個體化新的 EventArgs 類別。

如果事件沒有自訂資料,它可以將衍生自 EventArgs 之類別的執行個體傳遞至事件處理常式。 視事件傳遞至處理常式的確切資料而定,您或許能夠使用 .NET Framework 中現有的事件資枓類別。 例如,如果您的事件處理常式允許取消與事件關聯的動作,則您可以使用 CancelEventArgs 類別。

當您需要提供自訂資料給處理常式但沒有可用的現有類別時,您可以定義自己的事件資料類別。 這個類別必須衍生自 System.EventArgs。 依照慣例,這個類別會命名為 EventNameEventArgs。 下列範例說明這種自訂事件資料類別。 這個範例定義一個名為 AlarmEventArgs 的類別,這個類別提供兩個資料項目給事件處理常式:一個是唯讀 Time 屬性,表示警示響起的時間,另一個是 Snooze 屬性,表示是否應該在指定的間隔後再次發出警示,或是否應該取消未來的警示。

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; }
   }
};

定義事件的委派

事件委派用於定義事件的簽章。 特定的事件委派通常對應至特定的事件資料類別。 依照慣例,.NET Framework 中的事件具有簽章 EventName(sender, e),其中 sender 是 Object,負責提供對於引發事件之類別或結構的參考,而 e 是 EventArgs 物件或衍生自 EventArgs 的物件,負責提供事件資料。 委派定義通常採用 EventNameHandler(sender, e) 形式。

如果您使用已在 .NET Framework 類別庫或協力廠商類別庫中定義的事件資料類別,則該類別庫中很可能也有定義對應的事件委派。 例如,EventHandler 委派可以搭配 EventArgs 類別一起使用。 同樣地,CancelEventHandler 委派可以搭配 CancelEventArgs 類別一起使用。

如果您定義自訂事件資料類別,則您也可以定義自訂委派來定義事件簽章,或使用泛型 Action<T1, T2> 委派。

下列範例定義名為 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);

定義類別來引發事件

引發事件的類別必須提供事件宣告,也必須定義引發事件的方法。 此外,這個類別還必須在類別屬性或方法中提供某些邏輯來引發事件。

您可以在類別中使用 C# 的 event 關鍵字或 Visual Basic 的 Event 陳述式來定義事件成員。 當編譯器在您的類別中遇到事件宣告時,它會建立私用成員,例如:

private EventNameHandler eh = null;

編譯器也會建立兩個公用方法,即 add_EventName 和 remove_EventName。 這些方法是可以讓委派結合或從事件委派 eh 中移除的事件攔截程序 (Hook)。 這些詳細資料是程式設計人員所看不到的。

注意事項注意事項

在 C# 和 Visual Basic 2005 以外的語言中,編譯器可能不會自動產生對應事件成員的程式碼,因此您可能必須明確地定義事件攔截程序和私用委派欄位。

下列範例宣告名為 AlarmEvent 的事件。 這個範例節錄自名為 Alarm 之類別的範例,其完整原始程式碼如下所示。 請注意,這個類別具有 AlarmEventHandler 委派的簽章。

Event AlarmEvent As AlarmEventHandler
public event AlarmEventHandler AlarmEvent;
public:
   event AlarmEventHandler^ AlarmEvent; 

一旦定義了事件實作,您就必須決定何時要引發事件。 您可以在定義事件的類別或衍生類別中,呼叫保護的 OnEventName 方法,以引發事件。 然後,OnEventName 方法會引發事件。

注意事項注意事項

保護的 OnEventName 方法也允許衍生類別覆寫事件,而不需要將委派附加至該事件。衍生類別一定要呼叫基底類別的 OnEventName 方法,以確保註冊的委派可以接收該事件。

下列範例定義 OnAlarmEvent 方法,這個方法負責引發 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);
   }

下列範例定義名為 Set 的方法,這個方法包含邏輯來透過呼叫 OnAlarmEvent 方法,以引發事件。 如果警示時間的小時和分鐘等於目前時間的小時和分鐘,則 Set 方法會執行個體化 AlarmEventArgs 物件,並提供發出警示的時間給這個物件。 在事件處理常式執行之後,它會檢查 Snooze 屬性的值。 如果 Snooze 是 false,則不會再引發警示事件,所以 Set 方法可以結束。 如果 Snooze 是 true,則會依 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);
}

下列範例包含 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);
   }
};

請參閱

工作

HOW TO:引發和使用事件

HOW TO:在您的類別中實作事件

概念

事件和委派

其他資源

處理和引發事件