C#基礎教程:事件

pswyjz發表於2021-09-09

事件定義的時候,可以使用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. /// 

      

  8. /// 定義一個事件,當伺服器正常啟動後,觸發該事件   

  9. ///    

  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. /// 

      

  29. /// 定義一個事件,當伺服器正常結束後,觸發該事件   

  30. ///    

  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. /// 

      

  12. /// 定義一個事件,當伺服器正常啟動後,觸發該事件   

  13. ///    

  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. /// 

      

  35. /// 定義一個事件,當伺服器正常結束後,觸發該事件   

  36. ///    

  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.  

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/755/viewspace-2801046/,如需轉載,請註明出處,否則將追究法律責任。

相關文章