2009-06-26 84 views
2

我有一個類封裝了另一個類,並公開了它包裝的類中的幾個事件。 (它包裝的情況下可以改變)添加一個事件處理程序到另一個

我用下面的代碼:

public event EventHandler AnEvent; 

public OtherClass Inner { 
    get { /* ... */ } 
    set { 
     //... 
     if(value != null) 
      value.AnEvent += AnEvent; 
     //... 
    } 
} 

然而,事件引發不一致。

這段代碼有什麼問題?

回答

3

問題是Delegate是不可變的。

如果添加了處理程序事件時,它會創建一個新的Delegate實例其中包含了舊處理器和新添加的處理程序。舊的Delegate未被修改並被丟棄。

當我編寫value.AnEvent += AnEvent時,它將包含當前處理程序(如果有)的Delegate添加到內部類的事件中。但是,對外部類事件的更改將被忽略,因爲它們不會更改我添加到內部類事件中的Delegate實例。同樣,如果在設置Inner屬性後刪除處理程序,則不會從內部類的事件中刪除該處理程序。


有兩種正確的方法可以做到這一點。

我可以做我自己的處理程序調用包裝的情況下,像這樣:

public event EventHandler AnEvent; 

public OtherClass Inner { 
    get { /* ... */ } 
    set { 
     if(Inner != null) 
      Inner.AnEvent -= Inner_AnEvent; 

     //... 

     if(value != null) 
      value.AnEvent += Inner_AnEvent; 

     //... 
    } 
} 

void Inner_AnEvent(object sender, EventArgs e) { 
    var handler = AnEvent; 
    if (handler != null) handler(sender, e); 
} 

的另一種方法是創建一個自定義事件,增加了其處理程序的內部類的事件包裝,像這樣:

EventHandler anEventDelegates 

public OtherClass Inner { 
    get { /* ... */ } 
    set { 
     //... 
     if(value != null) 
      value.AnEvent += anEventDelegates; 
     //... 
    } 
} 
public event EventHandler AnEvent { 
    add { 
     anEventDelegates += value; 
     if (Inner != null) Inner.AnEvent += value; 
    } 
    remove { 
     anEventDelegates -= value; 
     if(Inner != null) Inner -= value; 
    } 
} 

請注意,這不是完全線程安全的。

我解決了這個問題,我和我發帖的問題&答案的人有類似問題的好處。

+2

其中,我認爲唯一的語義正確的方法是第一個。甚至超出誰是「發件人」財產應報告的問題(這可能是有點模糊的情況下收到訂閱的對象不是啓動動作的對象)還有一個問題o f如果一個對象爲一個內部和外部類實例的事件訂閱方法,然後取消訂閱一個方法,會發生什麼。即使重複取消一個實例的事件,也不應取消訂閱另一個事件。 – supercat 2012-05-22 19:02:33

3

your answer - 這裏有兩個問題...

第一:在這兩種情況下,你提出了錯誤的發送者的外部事件。有人訂閱外部類別的活動,預計這些類別將通過該外部類別的發件人進行募集。

這對於winform控件或綁定列表實現,其中發件人用於識別共享處理程序的許多對象之間的對象尤爲重要。

這應該不是是這樣的:

void Inner_AnEvent(object sender, EventArgs e) { 
    var handler = AnEvent; 
    if (handler != null) handler(this, e); 
} 

第二(更小的)問題是,你正在服用了事件的內部類,即使外部類沒有訂戶。你可以修復這個更多的自定義處理...

private EventHandler anEvent; 
public event EventHandler AnEvent { 
    add { // note: not synchronized 
     bool first = anEvent == null; 
     anEvent += value; 
     if(first && anEvent != null && inner != null) { 
      inner.SomeEvent += Inner_AnEvent; 
     } 
    } 
    remove { // note: not synchronized 
     bool hadValue = anEvent != null; 
     anEvent -= value; 
     if(hadValue && anEvent == null && inner != null) { 
      inner.SomeEvent -= Inner_AnEvent; 
     } 
    } 
} 

(以及類似的代碼在內蒙古獲取/設置成只訂閱,如果我們有聽衆......

if(value != null && anEvent != null) 
    value.AnEvent += Inner_AnEvent; 

如果你有很多的outer-的實例,這可能是一個很大的保護但很少使用事件

+0

我不是在寫圖書館;我是唯一一個處理事件的人,我希望sender參數成爲內部類。 – SLaks 2009-06-26 12:24:32

相關問題