天天看點

正确調用事件處理程式

不管是剛接觸 C# 還是已經具有多年開發經驗的大部分人會覺得事件處理很簡單,隻需要把事件定義好然後在需要的時候出發它就可以了。其實這種想法是錯誤的,這裡面有很多需要注意的問題。下面這段代碼是大部分開發人員經常使用的定義事件處理程式的方法。

上面的代碼中存在一個嚴重的問題,當在對象上觸發 demo 事件時并沒有關聯的事件處理程式的話,C# 将會用 null 值來表示沒有處理程式與該事件相關聯,進而将會引發 NullReferenceException 異常。針對這個問題大部分程式員會做如下修改:

這種修改方法解決了上述大部分問題,但是還存在一個隐藏的問題。當有多個線程都調用這個事件是就會出現線程之間互相争奪,舉個例子來說就是線程 A 在執行到 <code>if (demo!=null)</code>時發現 demo 不等于 null ,正巧這時線程 B 将唯一的事件處理程式解除了訂閱,這時線程 A 再調用 demo 時事件處理程式已經變為了 null ,進而導緻 NullReferenceException 異常。真多這個問題一些程式員又會做如下的修改:

上述這種方法是對等号右側的内容進行了淺拷貝建立了新的引用,使其指向原來的事件處理程式(相當于給事件訂閱者生成了一個快照),當另一個程序登出掉事件處理程式時,登出的隻是 demo 上所綁定的處理程式,是以當目前的線程執行 handler 時是不會出現 NullReferenceException 異常。這種解決方法是網上所能搜的方法之一,也是絕大部分開發人員所推薦的解決方法。但是這個方法會使代碼顯得難以了解(尤其是對于開發新手),并且代碼稍顯備援。于是在 C# 6.0 中微軟為我們增加了 null 條件運算符(?.)。null 條件運算符可以安全的調用事件處理程式并且使代碼清晰明了還簡單。首先它會判斷運算符左側的内容是否為 null ,如果是 null 就跳過該語句,反之執行運算符右側的内容。下面我們利用 null 條件運算符對前面的代碼進行一下改進。

Tip:使用 null 條件運算符有一點需要注意,運算符右側不允許直接出現括号,是以必須使用 Invoke 進行觸發事件。每定義一個委托或者時間編譯器就會生成一個 Invoke 方法。

進行觸發事件。每定義一個委托或者時間編譯器就會生成一個 Invoke 方法。