繁体   English   中英

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

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

在某些情况下,我非常喜欢静态事件,但事实上我很少在其他人的代码中看到它们,这让我想知道我是否遗漏了一些重要的东西。 我在这个网站上发现了很多关于静态事件的讨论,但是大多数都讨论了我不感兴趣的情况(比如静态类)或者我不想考虑使用它们的情况。

感兴趣的是,我可能有东西,并长期居住“经理”对象,它反应在这些情况下的东西的一个实例很多情况下的情况。 一个非常简单的例子来说明我的意思:

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类可以“看到”God-type类,我有时会使用NotifyGodOfPrayer()方法(这是几个类似问题中的首选答案),但是Believer类型的类通常是“模型” - 我不能或不想直接访问神级的组件。

这种方法有什么实际缺点吗?

编辑:感谢所有已经花时间回答的人。 我的例子可能不好,所以我想澄清一下我的问题:

如果我在这种情况下使用这种静态事件

  • 我确信只会有一个subscriber-object实例
  • 只要应用程序正在运行,就可以保证存在
  • 我正在观看的实例数量巨大

那么我不知道这种方法存在潜在的问题吗?

除非这个问题的答案是肯定的,否则我并不是真的在寻找替代实现,尽管我非常感谢所有想要提供帮助的人。 我不是在寻找最漂亮的解决方案(我必须将这个奖项提供给我自己的版本只是为了简短易读和维护:)

了解事件的一个重要事项是,它们会导致挂钩事件的对象在事件所有者被垃圾收集之前不被垃圾收集,或者直到事件处理程序被取消挂钩。

把它放到你的例子中,如果你有一个有多神的多神教神殿,你在那里提升和降级诸如

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

当他们与Believer.Prayed相连时,众神会留在你的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类的适当方法。 要管理引用并避免内存泄漏,您可以创建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));
    }
}

快速提示:临时存储事件委托,因为侦听器可能会取消其事件处理程序

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

编辑

考虑到你添加了关于你的场景(大量的事件调用者,常量和单个事件观察者)的细节,我认为你选择了正确的场景,纯粹出于效率原因。 它创建了最少的内存和CPU开销。 我不会采用这种方法,但对于你所描述的静态事件,我可能会采取非常实用的解决方案。

我看到的一个缺点是控制流程。 如果您的事件监听器是按照您所说的单个实例创建的,那么我将利用单身(反)模式并直接从Believer调用God方法。

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

为什么? 因为那时你可以更精细地控制执行祈祷的动作。 如果你决定将Believer子类Believer一种在某些日子里没有祈祷的特殊类型,那么你就可以控制它。

我实际上认为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
    }
}

暂无
暂无

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

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