2012-04-27 51 views
0

我有一個數據結構類是一個更大的數據/狀態類的孩子。.net事件混亂

內部數據結構在包含的數據更改時觸發事件。這個事件被更大的數據/狀態類使用。數據/狀態類然後觸發它自己的事件,以便它可以將附加信息傳遞給下一個事件處理程序。

Public class Data 
{ 
    //properties and objects go here 

    public int Count 
    { 
     get { return _count; } 
     internal set 
     { 
      //if the count grew simply set _count 
      if (value != _oldCount) 
      { 
       _oldCount = _count; 
       _count = value; 
      } 
      //if the count shrank then set the count and trigger an event if the count is under 100 
      else 
      { 
       _oldCount = _count; 
       _count = value; 
       if (_count < 100) 
       { 
        CountChanged(this, new EventArgs()); 
       } 
      } 
     } 
    } 
    public event EventHandler CountChanged; 
} 

上述事件此事件處理

Data.CountChanged += new EventHandler(DataCountChanged); 
private void DataCountChanged(object sender, EventArgs e) 
{ 
    DataRemoved(this, e); //Handle the old event and trigger a new event to pass along more information 
} 
public event EventHandler DataRemoved; 

最後第二個事件應該被另一事件處理程序進行處理做了一些工作消耗。不幸的是,觸發第二個事件的調用失敗了,而NullReferenceException往往不是。爲什麼?

----編輯---- 據我所知,檢查Null將防止異常。混亂就是爲什麼這個事件是擺在首位空= d

+0

簡單,沒有人訂閱它的事件處理程序。不在你的代碼片段中。 – 2012-04-27 01:46:31

+0

Data.CountChanged + = new EventHandler(DataCountChanged); 這不是事件的事件處理程序訂閱嗎? – Ritz 2012-04-27 02:23:09

+0

不,它在DataRemoved上被轟炸。 – 2012-04-27 03:19:57

回答

3

你應該總是提高使用下面的模式來避免空引用線程問題事件和:

private void DataCountChanged(object sender, EventArgs e) 
{ 
    var dr = DataRemoved; 
    if (dr != null) 
    { 
     dr(this, e); 
    } 
} 

原因的處理程序是空的,它應該被視爲作爲代表的特殊集合。當集合爲空時,委託具有空值。當您附加一個或多個處理程序時,集合不再爲空,因此不再爲空。

+0

這是最好的方式。如果多個線程正在執行並且可能(可能)將事件處理程序設置爲空,那麼這完全繞過了獲得空引用的問題。 – 2012-04-27 03:11:52

-3

有一個小竅門,以避免無效檢查:

public event YourDelegate MyEvent = delegate { }; 

這樣,你做的事:

如下只是初始化事件不需要檢查空值只是調用事件像往常一樣:編輯

this.MyEvent("Hi there!"); 

澄清:

聲明這樣一個事件:

public event Action MyEvent; 

它自動轉換爲:

private Action myEvent; 
public event Action MyEvent 
{ 
    add 
    { 
     this.myEvent += value; 
    } 
    remove 
    { 
     this.myEvent -= value; 
    } 
} 

因此初始化一個事件是這樣的:

public event Action MyEvent = delegate { }; 

這是安全,因爲外部代碼無法爲事件本身分配空值。

但是,您可以分配null以類是內部事件聲明但真正發生的事情是,要分配零由事件使用的私人代表。

來源:Jon Skeet, C# In Depth, Events

+0

據我所知,檢查Null將防止異常。令人困惑的是,爲什麼這個事件首先是空的= D – Ritz 2012-04-27 00:56:11

+2

這是一個不好的模式。該事件是可以通過外部代碼修改的公共字段。有人可以編寫'x.MyEvent = null;',你的代碼將失敗。在舉辦活動前,您應該始終做一項任務,然後進行空檢查。 – Enigmativity 2012-04-27 02:22:03

+0

我很感激你的建議,但在發佈之前你應該告訴你自己。我敢於嘗試爲事件賦予任何值=)至少在框架中4你不能做到這一點僅供參考,這是關於事件的基礎知識,事件不是字段,他們看起來像字段,但他們不是,他們的行爲像方法。閱讀本文:http://csharpindepth.com/Articles/Chapter2/Events.aspx我強烈建議您閱讀Jon Skeet部門的C#。引用:「人們經常發現難以發現事件與代表之間的差異」。 **您可以爲委託人分配空值,但不能爲事件** – Jupaol 2012-04-27 02:49:23

0
if(DataRemoved != null && DataRemoved.GetInvocationList().Length > 0) 
      { 
      } 
0

爲你的事件分配一個空的委託可能不是一個好的設計實踐。事件本質上就像函數指針一樣。換句話說,他們就像你班上的其他參考成員一樣。除非您爲其分配價值或訂閱它們,否則它們將會是且應該爲空。

您所得到的空引用異常與聲明private MyClass;的原因相同,然後在爲其分配值之前嘗試使用它。

當你訂閱一個事件時,你實質上是在告訴事件要調用哪個函數。如果你的事件沒有至少一個這樣的函數指針,它將不存在(NULL)。

+1

值得注意的是,與其他參考類型不同,事件/代表可以有多個值。這就是爲什麼我們使用'+ ='和' - ='符號進行賦值。通過這種方式,您可以讓事件調用多個訂閱功能。例如:'Form_Load1()'和'Form_Load2()',不是我會推薦做的。 – 2012-04-27 02:39:21