2009-07-12 65 views
6

我有以下情形。客戶端代碼只能訪問FooHandler,而不是直接訪問Foo實例。在C中實現鏈接事件的最佳方式是什麼?

public delegate void FooLoaded(object sender, EventArgs e); 

class Foo { 
    public event FooLoaded loaded; 
    /* ... some code ... */ 
    public void Load() { load_asynchronously(); } 
    public void callMeWhenLoadingIsDone() { loaded(this,EventArgs.Empty); } 
} 

class FooHandler { 
    public event FooLoaded OneFooLoaded; 

    /* ... some code ... */ 

    public void LoadAllFoos() { 
     foreach (Foo f in FooList) { 
      f.loaded += new FooLoaded(foo_loaded); 
      f.Load(); 
     } 
    } 

    void foo_loaded(object sender, EventArgs e) { 
     OneFooLoaded(this, e); 
    } 

} 

然後,客戶端將使用FooHandler類的OneFooLoaded事件來獲取加載foos的通知。這個'事件鏈'是正確的事情嗎?有沒有其他的選擇?我不喜歡這樣(感覺不對,我無法準確表達原因),但是如果我希望處理程序成爲訪問點,我似乎沒有多少選擇。

回答

4

如果因爲事件比必要的更加複雜和朝外的用於內部通信(我認爲至少是部分正確的考慮事件可以調用多個客戶,而你知道你只需要通知一個,右感覺錯了嗎? ),那麼我建議下面的選擇。而不是使用事件進行溝通的Foo到FooHandler完成,因爲foo是內部反正,你可以回調參數添加到富的構造函數或Load方法,當載入完成美孚可以撥打。如果你只有一個回調函數,這個參數可以只是一個函數,或者如果你有很多回調函數,它可以是一個接口。以下是我想你的代碼看起來與簡化內部接口:

public delegate void FooLoaded(FooHandler sender, EventArgs e); 

class Foo 
{ 
    Action<Foo> callback; 
    /* ... some code ... */ 
    public void Load(Action<Foo> callback) { this.callback = callback; load_asynchronously(); } 
    public void callMeWhenLoadingIsDone() { callback(this); } 
} 

class FooHandler 
{ 
    public event FooLoaded OneFooLoaded; 

    /* ... some code ... */ 

    public void LoadAllFoos() 
    { 
    foreach (Foo f in FooList) 
    { 
     f.Load(foo_loaded); 
    } 
    } 

    void foo_loaded(Foo foo) 
    { 
    // Create EventArgs based on values from foo if necessary 
    OneFooLoaded(this, null); 
    } 

} 

注意,這也可以讓你與FooLoaded委託進行更強類型。

另一方面,如果感覺錯誤,因爲該事件不應該經過FooHandler去找客戶,那麼1)我會質疑,因爲如果客戶不想處理個人富的對象,不應該在這個水平灌入從他們的活動,和2)如果你真的想這樣做,你可以實現在富一些公共的回調接口,即使富是私有的,或者使用像帕維爾機制建議。我想,但是,客戶喜歡落實更少事件處理程序和區分一個處理器內的源,而不是從幾十個更小的物體的連接(也可能斷開)事件的簡單性。

+0

我想開始我目前工作的項目之前,我曾見過這個簡單得多,這讓我的事情變得如此簡單(並且更容易進行單元測試)。 – ForbesLindesay 2010-12-17 15:06:46

1

我可以告訴你,這種事件瀑布是我多次相當自然地到達的東西,我還沒有遇到與他們有嚴重問題。

儘管我不認爲自己曾經透明地傳遞過事件,但總是伴隨着語義變化。例如,FooLoaded將變成AllFoosLoaded。如果您只是爲了實現這種語義變化,您可以將OneFooLoaded更改爲百分比指示符(接收類是否需要知道裝入了多少個Foo?)。

我認爲這樣的結構感覺不對,因爲event是用於廣播。它並沒有真正對廣播它的班級施加合同,也沒有對訂閱該班級的班級強加合同。

但是,正面類和信息隱藏的一般原則旨在促進合同的執行。

我還在收集我對這個問題的想法,對不起,如果上面有點不清楚,但我不知道是否有更好的方法來做你想做的事。如果有的話,我有興趣看到它,因爲你是。

1

你可以在事件委託addremove,而不是加薪:

class FooHandler { 
    public event FooLoaded OneFooLoaded { 
     add { 
      foreach (Foo f in FooList) { 
       f.loaded += new FooLoaded(value); 
      } 
     } 
     remove { 
      foreach (Foo f in FooList) { 
       f.loaded -= new FooLoaded(value); 
      } 
     } 
    } 

    public void LoadAllFoos() { 
     foreach (Foo f in FooList) { 
      f.Load(); 
     } 
    } 
} 

上述假設FooList是不可變的FooHandler壽命。如果它是可變的,那麼你還必須跟蹤對它添加/刪除項目,並相應地添加/刪除處理程序。

3

一個不同的方法是在所有事件都經過的域中創建一個單一的點(一個類)。任何使用該域的類都將連接到該類,該類有一個靜態事件列表,並且該類中的任何內部類事件都將由該類監聽,從而至少避免了該域中的事件鏈接。

參考文獻:

+0

我剛剛發現這一點,並認爲它太棒了,這麼多的不是試圖線了2點或3的事件,並得到意大利麪條 – Calanus 2009-07-31 11:03:59

2

夫婦的提示,可能會或可能不會有幫助的......

寫事件的聲明是這樣的:

public event FooLoaded loaded = delegate {}; 

即使沒有客戶入伍,您也可以安全地開火。

在鏈接事件的主題,當你有兩個事件:

public event EventHandler a = delegate {}; 
public event EventHandler b = delegate {}; 

您可能希望B的發射也引起的射擊:

b += (s, e) => a(s, e); 

然後您會看這一點,認爲這將是更簡潔地說:

b += a; 

事實上,ReSharper的甚至會建議它給你!但它意味着完全不同的東西。它將當前內容附加到ab,所以如果後面的更多處理程序加入a,這將不會導致它們在b被觸發時被調用。

相關問題