• C#基础:事件(四)


    事件定义的时候,可以使用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?
    1. private readonly object syncRoot_Started = new object();   
    2. private readonly object syncRoot_Stopped = new object();   
    3.   
    4. private ServerEventHandler m_StartedEventHandler;   
    5. private ServerEventHandler m_StoppedEventHandler;   
    6.   
    7. /// <summary>   
    8. /// 定义一个事件,当服务器正常启动后,触发该事件   
    9. /// </summary>   
    10. public event ServerEventHandler Started   
    11. {   
    12.     add   
    13.     {   
    14.         lock (syncRoot_Started)   
    15.         {   
    16.             m_StartedEventHandler += value;   
    17.         }   
    18.     }   
    19.     remove   
    20.     {   
    21.         lock (syncRoot_Started)   
    22.         {   
    23.             m_StartedEventHandler -= value;   
    24.         }   
    25.     }   
    26. }   
    27.   
    28. /// <summary>   
    29. /// 定义一个事件,当服务器正常结束后,触发该事件   
    30. /// </summary>   
    31. public event ServerEventHandler Stopped   
    32. {   
    33.     add   
    34.     {   
    35.         lock (syncRoot_Stopped)   
    36.         {   
    37.             m_StoppedEventHandler += value;   
    38.         }   
    39.     }   
    40.     remove   
    41.     {   
    42.         lock (syncRoot_Stopped)   
    43.         {   
    44.             m_StoppedEventHandler -= value;   
    45.         }   
    46.     }   
    47. }   
    48.   
    49. protected virtual void DoStarted(object sender, ServerEventArgs e)   
    50. {   
    51.     if (m_StartedEventHandler != null)   
    52.         m_StartedEventHandler(sender, e);   
    53. }   
    54.   
    55. protected virtual void DoStopped(object sender, ServerEventArgs e)   
    56. {   
    57.     if (m_StoppedEventHandler != null)   
    58.         m_StoppedEventHandler(sender, e);   
    59. }   

    现在,我们新加入了用于同步的对象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?
    1. private readonly object syncRoot_Started = new object();   
    2. private readonly object syncRoot_Stopped = new object();   
    3.   
    4. //private ServerEventHandler m_StartedEventHandler;   
    5. //private ServerEventHandler m_StoppedEventHandler;   
    6. private readonly object eventStarted = new object();   
    7. private readonly object eventStopped = new object();   
    8.   
    9. private EventHandlerList eventHandlerList = new EventHandlerList();   
    10.   
    11. /// <summary>   
    12. /// 定义一个事件,当服务器正常启动后,触发该事件   
    13. /// </summary>   
    14. public event ServerEventHandler Started   
    15. {   
    16.     add   
    17.     {   
    18.         lock (syncRoot_Started)   
    19.         {   
    20.             // m_StartedEventHandler += value;   
    21.             eventHandlerList.AddHandler(eventStarted, value);   
    22.         }   
    23.     }   
    24.     remove   
    25.     {   
    26.         lock (syncRoot_Started)   
    27.         {   
    28.             // m_StartedEventHandler -= value;   
    29.             eventHandlerList.RemoveHandler(eventStarted, value);   
    30.         }   
    31.     }   
    32. }   
    33.   
    34. /// <summary>   
    35. /// 定义一个事件,当服务器正常结束后,触发该事件   
    36. /// </summary>   
    37. public event ServerEventHandler Stopped   
    38. {   
    39.     add   
    40.     {   
    41.         lock (syncRoot_Stopped)   
    42.         {   
    43.             // m_StoppedEventHandler += value;   
    44.             eventHandlerList.AddHandler(eventStopped, value);   
    45.         }   
    46.     }   
    47.     remove   
    48.     {   
    49.         lock (syncRoot_Stopped)   
    50.         {   
    51.             // m_StoppedEventHandler -= value;   
    52.             eventHandlerList.RemoveHandler(eventStopped, value);   
    53.         }   
    54.     }   
    55. }   
    56.   
    57. protected virtual void DoStarted(object sender, ServerEventArgs e)   
    58. {   
    59.     ServerEventHandler startedHandler = (ServerEventHandler) eventHandlerList[eventStarted];   
    60.     if (startedHandler != null)   
    61.         startedHandler(sender, e);   
    62. }   
    63.   
    64. protected virtual void DoStopped(object sender, ServerEventArgs e)   
    65. {   
    66.     ServerEventHandler stoppedHandler = (ServerEventHandler) eventHandlerList[eventStopped];   
    67.     if (stoppedHandler != null)   
    68.         stoppedHandler(sender, e);   
    69. }   
    70.  
  • 相关阅读:
    浅析BIO、NIO、AIO
    equals()和hashCode()区别
    mysql分区
    java反射
    设计模式
    两个线程一个生产者个一个消费者
    Redis事务
    常用面试题
    springboot整合redis(注解形式)
    ElasticSearch6更新与重大变化
  • 原文地址:https://www.cnblogs.com/daxnet/p/1687002.html
Copyright © 2020-2023  润新知