2014-02-14 236 views
7

有些情況下我很喜歡靜態事件,但是我很少在其他人的代碼中看到它們,這讓我懷疑我是否錯過了一些重要的東西。我在這個網站上發現了很多關於靜態事件的討論,但是大多數討論的是我不感興趣的場景(比如靜態類),或者我不會考慮首先使用它們。C# - 非靜態類的靜態事件

我對感興趣的是我可能有許多事情的實例和一個長壽命的「經理」對象的實例,這些實例對這些實例的某些事情做出反應。一個非常簡單的例子來說明我的意思:

public class God { 

    //the list of followers is really big and changes all the time, 
    //it seems like a waste of time to 
    //register/unregister events for each and every one... 
    readonly List<Believer> Believers = new List<Believer>(); 

    God() { 
     //...so instead let's have a static event and listen to that 
     Believer.Prayed += this.Believer_Prayed; 
    } 

    void Believer_Prayed(Believer believer, string prayer) { 
     //whatever 
    } 
} 

public class Believer { 

    public static event Action<Believer, string> Prayed; 

    void Pray() { 
     if (Prayed != null) { 
      Prayed(this, "can i have stuff, please"); 
     } 
    } 
} 

對我來說,這看起來像比有一個實例事件更清潔和更簡單的解決方案,我沒有要麼監測信徒收集的變化。在Believer類可以「看見」上帝類的情況下,我有時可能會使用NotifyGodOfPrayer()方法(這是幾個類似問題中的首選答案),但通常Believer類類是在「模型」 - 我不能或不想直接訪問上帝類的裝配。

這種方法有什麼缺點嗎?

編輯:感謝大家誰已經花時間回答。 我的例子可能是壞的,所以我想澄清我的問題:如果我用這種靜態的事件的情況下,在

  • 我敢肯定會有永遠只能是一個實例

    這是保證只要存在,因爲應用程序的用戶對象

  • 運行
  • ,我看實例的數量是巨大的

然後是日這種方法的潛在問題我不知道?

除非對這個問題的答案是肯定的,否則我並不是在尋找其他的實現方式,儘管我非常感謝所有試圖提供幫助的人。 我不是尋找最漂亮的解決方案(我不得不放棄這個獎項對我自己的版本只是爲了做空,易於閱讀和維護:)

+0

可能的重複[在C#中靜態事件如何與非靜態事件進行比較?](http://stackoverflow.com/questions/7045595/how-do-static-events-compare-to-non-static- events-in-c) –

+1

可能,但沒有一個答案對我的方案有用。 答案1(接受):「不發送靜態事件的實例」 - 不是這裏的情況 答案2:取消訂閱有問題 - 這裏不是這種情況 答案3:靜態方法/類 - 不感興趣 答案4:3使用靜態事件的案例 - 他們都不是我的使用案例 答案5:「靜態事件就像是完全可怕的」 - 是的,沒關係。 – wilford

回答

12

一個重要的事情瞭解的事件是他們的原因是垃圾回收,或直到事件處理脫鉤,其鉤住事件不被收集到事件所有者垃圾對象。

把它放到你的例子,如果你有很多神靈一個多神教萬神殿,在那裏你提升和降級的神如

new God("Svarog"); 
new God("Svantevit"); 
new God("Perun"); 

神將留在你的RAM,而它們所連接到Believer.Prayed。這會導致你的應用程序泄漏神。


我還對設計決策發表意見,但我明白你所做的例子是,也許不是你的真實的情景中最好的副本。

對我來說,不要創建從GodBeliever的依賴關係並使用事件似乎更合理。好的方法是創建一個站在信徒和神之間的事件聚合器。例如:被稱爲

public interface IPrayerAggregator 
{ 
    void Pray(Believer believer, string prayer); 
    void RegisterGod(God god); 
} 

// god does 
prayerAggregator.RegisterGod(this); 
// believer does 
prayerAggregator.Pray(this, "For the victory!"); 

Pray方法,事件聚合依次調用God類的適當的方法。要管理的參考和避免內存泄漏,您可以創建在weak references

public class Priest : IPrayerAggregator 
{ 
    private List<WeakReference> _gods; 

    public void Pray(Believer believer, string prayer) 
    { 
     foreach (WeakReference godRef in _gods) { 
      God god = godRef.Target as God; 
      if (god != null) 
       god.SomeonePrayed(believer, prayer); 
      else 
       _gods.Remove(godRef); 
     } 
    } 

    public void RegisterGod(God god) 
    { 
     _gods.Add(new WeakReference(god, false)); 
    } 
} 

快速提示的集合UnregisterGod方法或保持神:暫時存儲事件委託作爲聽衆可能解開他們的事件處理程序

void Pray() { 
    var handler = Prayed; 
    if (handler != null) { 
     handler(this, "can i have stuff, please"); 
    } 
} 

編輯

考慮到您添加的關於您的場景的細節(大量的事件調用者,常量和單個事件觀察者)我認爲您選擇了正確的場景,純粹是出於效率原因。它創建最少的內存和CPU開銷。我不會一般採用這種方法,但對於您描述的靜態事件而言,這是我可能採取的非常實用的解決方案。

我看到的一個缺點是控制流。如果您的事件偵聽器是按照您的說法在單個實例中創建的,那麼我會利用singleton(反)模式並直接調用Believer中的God方法。

God.Instance.Pray(this, "For the victory!"); 
//or 
godInstance.Pray(this, "For the victory!"); 

爲什麼?因爲那樣你就可以更好地執行祈禱行動了。如果你決定將Believer的子類別劃分爲某種特定類型的特定日子,那麼你就可以控制這一點。

+0

+1對於弱引用 – Tseng

+0

我喜歡你的上帝的名字:) 在我的實際應用程序中,「上帝」會像「AutoSaveService」那樣,我確信在應用程序生命週期中只有一個實例只有一個實例,所以多個實例或註銷靜態事件不是一個問題。我喜歡聚合器的例子,我可能實際上不會經常使用這種方法。 通常我會存儲處理程序(並有一個專用的「OnPrayed」方法),我只是編輯它以縮短示例。 – wilford

+0

謝謝:)無論如何,聚合器的例子可能有點太冗長。通用的聚合器可能是有用的。一個可以接收不同事件的通知,並通知不同的聽衆。 [我喜歡Caliburn.Micro如何處理這個問題](http://caliburnmicro.codeplex.com/wikipage?title=The%20Event%20Aggregator)。除了可能的內存泄漏,如果使用不當,我沒有看到你的方法有問題。 –

0

我倒認爲having an instance even將是更清潔和挑釁更具可讀性。
將它視爲一個實例正在捕捉,它更簡單,所以他的祈禱事件得到觸發。我不認爲這是螞蟻的缺點。我不認爲monitor changes不是監控靜態事件的喧囂。但走的正確方法...

監控名單:
更改列表是一個ObservableCollection(和看一看NotifyCollectionChangedEventArgs)。
通過監視它:

public class God { 

    readonly ObservableCollection<Believer> Believers = new ObservableCollection<Believer>(); 

    public God() { 
     Believers = new ObservableCollection<T>(); 
     Believers.CollectionChanged += BelieversListChanged; 
    } 

    private void BelieversListChanged(object sender, NotifyCollectionChangedEventArgs args) { 

     if ((e.Action == NotifyCollectionChangedAction.Remove || e.Action ==  NotifyCollectionChangedAction.Replace) && e.OldItems != null) 
     { 
      foreach (var oldItem in e.OldItems) 
      { 
       var bel= (Believer)e.oldItem;    
       bel.Prayed -= Believer_Prayed; 
      } 
     } 

     if((e.Action==NotifyCollectionChangedAction.Add ||    e.Action==NotifyCollectionChangedAction.Replace) && e.NewItems!=null) 
      { 
       foreach(var newItem in e.NewItems) 
       { 
        foreach (var oldItem in e.OldItems) 
       { 
        var bel= (Believer)e.newItem;    
        bel.Prayed += Believer_Prayed; 
       } 
      } 
     } 
    } 

    void Believer_Prayed(Believer believer, string prayer) { 
     //whatever 
    } 
} 
+0

ObservableCollection是我經常這樣做的(順便說一句,不要忘記處理Reset-Action),但請注意它有多少代碼。如果我有100。000追隨者,它讓我覺得在啓動時循環遍歷所有這些事件並註冊實例事件感覺很愚蠢。如果我想使用Believers集合進行綁定,無論如何都必須使用ObservableCollection,但看起來像是一種浪費,因此我可以註冊事件。 – wilford