事件定义的时候,可以使用add和remove关键字来自定义事件处理函数的添加与移除功能。例如,可以在添加和移除之前,使用lock关键字实现线程同步。虽然MethodImplAttribute会用当前类的对象作为同步对象实现线程同步,但当对象需要向外界公布多个事件的时候,这样做会产生效率问题。比如:对象A向外界公布了E1、E2两个事件,订阅方O1使用+=运算符试图订阅E1事件;订阅方O2也使用+=运算符试图订阅E2事件。假设这两个订阅操作同时进行,那么无论谁先抢到订阅权,另一个操作不得不等待,直到前一个订阅操作成功完成。这是因为,MethodImplAttribute会将A用作线程同步的锁定对象;对于O1和O2而言,在订阅事件的时候,是共用同一个锁定对象的。MethodImplAttribute另一个问题在于,如果A是一个值对象,那么就根本没法使用A作为锁定对象,因为A根本没有“同步索引”,因此你就无法使用多线程去同步使用这样的对象,即使是使用了MethodImplAttribute,也只不过是一个摆设。
仍然以EventDemo项目为例,我们将该案例中Server类的事件定义部分稍作改动,将其改为下面的形式:
view plaincopy to clipboardprint?
private readonly object syncRoot_Started = new object();
private readonly object syncRoot_Stopped = new object();
private ServerEventHandler m_StartedEventHandler;
private ServerEventHandler m_StoppedEventHandler;
/// <summary>
/// 定义一个事件,当服务器正常启动后,触发该事件
/// </summary>
public event ServerEventHandler Started
{
add
{
lock (syncRoot_Started)
{
m_StartedEventHandler += value;
}
}
remove
{
lock (syncRoot_Started)
{
m_StartedEventHandler -= value;
}
}
}
/// <summary>
/// 定义一个事件,当服务器正常结束后,触发该事件
/// </summary>
public event ServerEventHandler Stopped
{
add
{
lock (syncRoot_Stopped)
{
m_StoppedEventHandler += value;
}
}
remove
{
lock (syncRoot_Stopped)
{
m_StoppedEventHandler -= value;
}
}
}
protected virtual void DoStarted(object sender, ServerEventArgs e)
{
if (m_StartedEventHandler != null)
m_StartedEventHandler(sender, e);
}
protected virtual void DoStopped(object sender, ServerEventArgs e)
{
if (m_StoppedEventHandler != null)
m_StoppedEventHandler(sender, e);
}
现在,我们新加入了用于同步的对象syncRoot_Started和syncRoot_Stopped,它们被定义为Server的私有只读成员;在定义事件处理列表添加与移除的逻辑里,使用lock关键字实现线程同步,确保对于同一个事件的调用列表,在同一时刻只有一个线程对其进行操作。在前面的事件实现过程中,由于我们使用默认的add和remove方法,因此C#编译器会自动生成一个类似于上述代码中m_StartedEventHandler、m_StoppedEventHandler的私有成员,而在自定义的实现方式里,开发人员必须手工添加这样的私有成员。
C#中的属性可以是只包含get的只读属性,可以是只包含set的只写属性,还可以是既包含get又包含set的读写属性;而event的定义不同,add和remove必须成对出现。
还有一种情况下,会用add和remove来自定义事件的处理过程的添加与移除,就是当某个对象需要向外界公布多个事件时,此时,没有必要针对每个事件都定义一个私有成员,具体做法是,在类中定义一个集合(比如字典),在add中,向集合添加事件处理过程,而在remove中,将事件处理过程从集合中移除。我们再次改造上述实例,通过使用System.ComponentModel.EventHandlerList类来实现这样的效果:
view plaincopy to clipboardprint?
private readonly object syncRoot_Started = new object();
private readonly object syncRoot_Stopped = new object();
//private ServerEventHandler m_StartedEventHandler;
//private ServerEventHandler m_StoppedEventHandler;
private readonly object eventStarted = new object();
private readonly object eventStopped = new object();
private EventHandlerList eventHandlerList = new EventHandlerList();
/// <summary>
/// 定义一个事件,当服务器正常启动后,触发该事件
/// </summary>
public event ServerEventHandler Started
{
add
{
lock (syncRoot_Started)
{
// m_StartedEventHandler += value;
eventHandlerList.AddHandler(eventStarted, value);
}
}
remove
{
lock (syncRoot_Started)
{
// m_StartedEventHandler -= value;
eventHandlerList.RemoveHandler(eventStarted, value);
}
}
}
/// <summary>
/// 定义一个事件,当服务器正常结束后,触发该事件
/// </summary>
public event ServerEventHandler Stopped
{
add
{
lock (syncRoot_Stopped)
{
// m_StoppedEventHandler += value;
eventHandlerList.AddHandler(eventStopped, value);
}
}
remove
{
lock (syncRoot_Stopped)
{
// m_StoppedEventHandler -= value;
eventHandlerList.RemoveHandler(eventStopped, value);
}
}
}
protected virtual void DoStarted(object sender, ServerEventArgs e)
{
ServerEventHandler startedHandler = (ServerEventHandler) eventHandlerList[eventStarted];
if (startedHandler != null)
startedHandler(sender, e);
}
protected virtual void DoStopped(object sender, ServerEventArgs e)
{
ServerEventHandler stoppedHandler = (ServerEventHandler) eventHandlerList[eventStopped];
if (stoppedHandler != null)
stoppedHandler(sender, e);
}