2009-12-08 81 views
2

當事件沒有訂閱者時,如何確保在事件觸發時不會拋出異常。如何安全地觸發事件

// Delegate declaration 
public delegate void _delDisplayChange(object sender,string option); 

// Event declaration 
public event _delDisplayChange DisplayChange; 

//throwing the event 
DisplayChange(this, "DISTRIBUTION"); 
+2

我前段時間發佈了一個關於這個話題的問題,並且也爲它提供了相當多的火焰,但它可能是澄清一些問題的好地方:http://stackoverflow.com/questions/ 840715 /正確的方式提高事件在網絡框架 –

+0

感謝您的鏈接到問題 – Brad

+1

請注意,成功地避免例外只是你的憂慮的開始,而不是結束。如果您處於多線程狀態,您還需要擔心是否調用陳舊事件處理程序的*完全正交*問題,並且如果是這樣,請您正確處理該情況。有關詳細信息,請參閱http://blogs.msdn.com/ericlippert/archive/2009/04/29/events-and-races.aspx。 –

回答

18

這裏是推薦的方式做到這一點:

protected void RaiseDisplayChanged(string message) 
{ 
    var handlers = DisplayChange; 
    if(handlers != null) 
     handlers(this, message); 
} 

複製事件處理程序枚舉檢查做了兩件事之前:

  1. 如果DisplayChange處理程序成爲檢查和射擊之間空,你不要死
  2. 如果偵聽器在枚舉DisplayChange列表的同時修改DisplayChange列表,則不會遇到奇怪現象。

此外,您沒有使用標準的事件協議。您的代表應該是:

public delegate void DisplayChangeDelegate(object sender, OptionsEventArgs args); 

其中OptionsEventArgs從EventArgs派生。更進一步,在.Net 3.5中,你不應該像這樣定義一個委託。相反,你應該只定義您的事件:

public event EventHandler<OptionsEventArgs> DisplayChanged; 

我喜歡通過定義這個類來把它連一步:

public class EventArgs<T> : EventArgs 
{ 
    public T Payload { get; private set } 
    public EventArgs(T payload) 
    { 
     Payload = payload; 
    } 
} 

然後,你不需要定義OptionsEventArgs:

public event EventHandler<EventArgs<string>> DisplayChanged; 

只是一些東西去思考...

+0

謝謝你的回答...和附加信息 – Brad

+0

很好的答案,但它也值得解釋爲什麼支持字段可以成爲'null'(多線程),爲什麼值得做事件註冊並提出線程安全的開始。 –

+1

應考慮使'RaiseDisplayChanged(string)'虛擬,所以派生類可以重寫事件的內部行爲。 – walkingTarget

4

更改此:

// Event declaration  
public event _delDisplayChange DisplayChange; 

這樣:

// Event declaration  
public event _delDisplayChange DisplayChange = delegate{}; 

這將確保您的活動將始終有至少一個用戶。

+0

不需要添加一個虛擬用戶,該虛擬用戶佔用內存並且不遵守任何邏輯;檢查null與事件/訂戶邏輯的基本原理更加一致。 – CesarGon

+0

這是一個有趣的轉折,但像塞薩爾說,有更好的方法。 –

+0

什麼是「事件/訂戶邏輯的基本原理」,以及爲什麼檢查null與它一致? –

1

正如布賴恩說:許多來源建議作出檢查它不爲空事件之前的副本:

_delDisplayChange displayChangeCopy = DisplayChange; 
if (displayChangeCopy != null) 
    displayChangeCopy(this, "DISTRIBUTION"); 

這有助於使你的代碼更加線程安全的,因爲displayChangeCopy的值不會空校驗,並且在調用之間改變。

+2

這不會讓你的代碼是線程安全的。它消除了一個競賽條件。它並沒有消除更一般的競爭條件,即DisplayChange的值存儲與本地副本的調用之間* DisplayChange的內容可能已經改變*,因此*您現在正在調用一個可能依賴的函數在被摧毀的狀態*。不要在沒有明確描述你正在緩解的線程危險的情況下聲明代碼是「線程安全的」。你幾乎沒有減輕這裏所有的危險。 –

+0

同意 - 我已編輯帖子,使其更清楚。 – Anton

1

除了Jay的回答,以下是link關於使用空代表時的性能考慮事項。