2014-02-14 189 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