简体   繁体   English

C# - 非静态类上的静态事件

[英]C# - Static events on non-static classes

There are situations where I'm quite fond of static events, but the fact that I rarely see them in other people's code makes me wonder if I'm missing something important. 在某些情况下,我非常喜欢静态事件,但事实上我很少在其他人的代码中看到它们,这让我想知道我是否遗漏了一些重要的东西。 I found a lot of discussions about static events on this site, but most of them deal with situations that I'm not interested in (like on static classes) or where I wouldn't think of using them in the first place. 我在这个网站上发现了很多关于静态事件的讨论,但是大多数都讨论了我不感兴趣的情况(比如静态类)或者我不想考虑使用它们的情况。

What I am interested in are situations where I might have many instances of something and a single instance of a long-living "manager" object that reacts to something on those instances. 感兴趣的是,我可能有东西,并长期居住“经理”对象,它反应在这些情况下的东西的一个实例很多情况下的情况。 A very simple example to illustrate what I mean: 一个非常简单的例子来说明我的意思:

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");
        }
    }
}

To me, this looks like a much cleaner and simpler solution than having an instance event and I don't have to monitor changes in the believers collection either. 对我来说,这看起来比一个实例事件更清晰,更简单的解决方案,我也不必监视信徒集合中的变化。 In cases where the Believer class can "see" the God-type class, I might sometimes use a NotifyGodOfPrayer()-method instead (which was the preferred answer in a few similar questions), but often the Believer-type class is in a "Models"-assembly where I can't or don't want to access the God class directly. 如果Believer类可以“看到”God-type类,我有时会使用NotifyGodOfPrayer()方法(这是几个类似问题中的首选答案),但是Believer类型的类通常是“模型” - 我不能或不想直接访问神级的组件。

Are there any actual downsides to this approach? 这种方法有什么实际缺点吗?

Edit: Thanks to everyone who has already taken the time to answer. 编辑:感谢所有已经花时间回答的人。 My example may be bad, so I would like to clarify my question: 我的例子可能不好,所以我想澄清一下我的问题:

If I use this kind of static events in situations, where 如果我在这种情况下使用这种静态事件

  • I'm sure there will only ever be one instance of the subscriber-object 我确信只会有一个subscriber-object实例
  • that is guaranteed to exist as long as the application is running 只要应用程序正在运行,就可以保证存在
  • and the number of instances I'm watching is huge 我正在观看的实例数量巨大

then are there potential problems with this approach that I'm not aware of? 那么我不知道这种方法存在潜在的问题吗?

Unless the answer to that question is "yes", I'm not really looking for alternative implementations, though I really appreciate everyone trying to be helpful. 除非这个问题的答案是肯定的,否则我并不是真的在寻找替代实现,尽管我非常感谢所有想要提供帮助的人。 I'm not looking for the most pretty solution (I'd have to give that prize to my own version simply for being short and easy to read and maintain :) 我不是在寻找最漂亮的解决方案(我必须将这个奖项提供给我自己的版本只是为了简短易读和维护:)

One important thing to know about events is that they cause objects which are hooked to an event not to be garbage collected until event owner is garbage collected, or until event handler is unhooked. 了解事件的一个重要事项是,它们会导致挂钩事件的对象在事件所有者被垃圾收集之前不被垃圾收集,或者直到事件处理程序被取消挂钩。

To put it into your example, if you had a polytheistic pantheon with many gods, where you promoted and demoted gods such as 把它放到你的例子中,如果你有一个有多神的多神教神殿,你在那里提升和降级诸如

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

gods would remain in your RAM while they are attached to Believer.Prayed . 当他们与Believer.Prayed相连时,众神会留在你的RAM中。 Believer.Prayed This would cause your application to leak gods. 这会导致你的应用程序泄露神。


I'll comment on design decision also, but I understand that example you made is maybe not best copy of your real scenario. 我也会评论设计决策,但我明白你所做的例子可能不是你真实场景的最佳副本。

It seems more reasonable to me not to create dependency from God to Believer , and to use events. 对我来说,不要创造从GodBeliever依赖,并使用事件似乎更合理。 Good approach would be to create an event aggregator which would stand between believers and gods. 好的方法是创建一个站在信徒和神之间的事件聚合器。 For example: 例如:

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!");

Upon Pray method being called, event aggregator calls appropriate method of God class in turn. 在调用Pray方法时,事件聚合器依次调用God类的适当方法。 To manage references and avoid memory leaks, you could create UnregisterGod method or hold gods in collection of weak references such as 要管理引用并避免内存泄漏,您可以创建UnregisterGod方法或在诸如的弱引用集合中保存神

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));
    }
}

Quick tip: Temporarily store event delegate as listeners might unhook their event handlers 快速提示:临时存储事件委托,因为侦听器可能会取消其事件处理程序

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

Edit 编辑

Having in mind details you added about your scenario (huge number of event invokers, constant and single event watcher) I think you chose right scenario, purely for efficiency reasons. 考虑到你添加了关于你的场景(大量的事件调用者,常量和单个事件观察者)的细节,我认为你选择了正确的场景,纯粹出于效率原因。 It creates least memory and cpu overhead. 它创建了最少的内存和CPU开销。 I wouldn't take this approach generally, but for scenario you described static event is very pragmatic solution that I might take. 我不会采用这种方法,但对于你所描述的静态事件,我可能会采取非常实用的解决方案。

One downside I see is flow of control. 我看到的一个缺点是控制流程。 If your event listener is created in single instance as you say, then I would leverage singleton (anti)pattern and invoke method of God from Believer directly. 如果您的事件监听器是按照您所说的单个实例创建的,那么我将利用单身(反)模式并直接从Believer调用God方法。

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

Why? 为什么? Because then you get more granular control over performing action of praying. 因为那时你可以更精细地控制执行祈祷的动作。 If you decide down the line that you need to subclass Believer to a special kind that doesn't pray on certain days, then you would have control over this. 如果你决定将Believer子类Believer一种在某些日子里没有祈祷的特殊类型,那么你就可以控制它。

I actually think that having an instance even would be cleaner and defiantly more readable. 我实际上认为having an instance even会更清晰,更具可读性。
It is much more simple to view it as, an instance is preying, so his pray event gets trigger. 查看它更简单,因为一个实例正在捕食,所以他的祈祷事件会被触发。 And I don't see ant downsides for that. 而且我没有看到蚂蚁的缺点。 I don't think that monitor changes is not more of a hustle than monitoring the static event. 我不认为monitor changes不是监视静态事件的喧嚣。 but is the correct way to go... 但是正确的方法......

Monitoring the list: 监控列表:
Change the list to be an ObservableCollection (and take a look at NotifyCollectionChangedEventArgs ). 将列表更改为ObservableCollection (并查看NotifyCollectionChangedEventArgs )。
Monitor it by: 监控它:

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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM