引发事件
如果希望您的类引发一个事件,您需要提供以下三个元素:
提供事件数据的类。
事件委托。
引发事件的类。
定义提供事件数据的类
按照 .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 合并或从该事件委托中移除。 这些详细信息对程序员是隐藏的。
备注
在除 C# 和 Visual Basic 2005 以外的其他语言中,编译器可能不会自动生成与事件成员对应的代码,您可能需要显式地定义事件挂钩和私有委托字段。
下面的示例声明一个名为 AlarmEvent 的事件。 该示例节选自名为 Alarm 的类的示例,其完整的源代码如下所示。 请注意,它具有 AlarmEventHandler 委托的签名。
Event AlarmEvent As AlarmEventHandler
public event AlarmEventHandler AlarmEvent;
public:
event AlarmEventHandler^ AlarmEvent;
定义事件实现后,您必须确定引发事件的时间。 通过在定义事件的类或派生类中调用受保护的 OnEventName 方法来引发事件。 随后,OnEventName方法引发事件。
备注
受保护的 OnEventName方法也允许派生类重写事件,而不必向其附加委托。派生类必须始终调用基类的 OnEventName方法以确保注册的委托接收到事件。
下面的示例定义负责引发 AlarmEvent 事件的 OnAlarmEvent 方法。
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);
}
};