引發事件
如果您希望類別引發事件,您必須提供下列三個項目:
提供事件資料的類別。
事件委派。
引發事件的類別。
定義類別來提供事件資料
在 .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);
}
};