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

wil*_*ord 9 c# events

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

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

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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对我来说,这看起来比一个实例事件更清晰,更简单的解决方案,我也不必监视信徒集合中的变化.如果Believer类可以"看到"God-type类,我有时会使用NotifyGodOfPrayer()方法(这是几个类似问题中的首选答案),但是Believer类型的类通常是"模型" - 我不能或不想直接访问神级的组件.

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

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

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

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

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

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

Nik*_*vić 13

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

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

new God("Svarog");
new God("Svantevit");
new God("Perun");
Run Code Online (Sandbox Code Playgroud)

当他们被连接时,众神将留在你的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!");
Run Code Online (Sandbox Code Playgroud)

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

编辑

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

我看到的一个缺点是控制流程.如果你所说的事件监听器是在单个实例中创建的,那么我将直接利用singleton(反)模式和调用Godfrom 方法Believer.

God.Instance.Pray(this, "For the victory!");
//or
godInstance.Pray(this, "For the victory!");
Run Code Online (Sandbox Code Playgroud)

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