C#基础:事件(四)
事件定义的时候,可以使用add和remove关键字来自定义事件处理函数的添加与移除功能。例如,可以在添加和移除之前,使用lock关键字实现线程同步。虽然MethodImplAttribute会用当前类的对象作为同步对象实现线程同步,但当对象需要向外界公布多个事件的时候,这样做会产生效率问题。比如:对象A向外界公布了E1、E2两个事件,订阅方O1使用+=运算符试图订阅E1事件;订阅方O2也使用+=运算符试图订阅E2事件。假设这两个订阅操作同时进行,那么无论谁先抢到订阅权,另一个操作不得不等待,直到前一个订阅操作成功完成。这是因为,MethodImplAttribute会将A用作线程同步的锁定对象;对于O1和O2而言,在订阅事件的时候,是共用同一个锁定对象的。MethodImplAttribute另一个问题在于,如果A是一个值对象,那么就根本没法使用A作为锁定对象,因为A根本没有“同步索引”,因此你就无法使用多线程去同步使用这样的对象,即使是使用了MethodImplAttribute,也只不过是一个摆设。
仍然以EventDemo项目为例,我们将该案例中Server类的事件定义部分稍作改动,将其改为下面的形式:
- 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类来实现这样的效果:
- 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);
- }