gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 17:43:21 25 4
gpt4 key购买 nike

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

感兴趣的是这样的情况:我可能拥有某物的许多实例和一个长期存在的“管理器”对象的单个实例,该对象对这些实例上的某物使用react。一个非常简单的例子来说明我的意思:

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 类的情况下,有时我可能会使用 NotifyGodOfPrayer() 方法(这是一些类似问题的首选答案),但 Believer 类通常位于“模型”- 我不能或不想直接访问上帝类的程序集。

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

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

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

  • 我确信订阅者对象只会有一个实例
  • 只要应用程序运行就保证存在
  • 而且我正在观看的实例数量巨大

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

除非该问题的答案是"is",否则我并不是真的在寻找替代实现,但我真的很感谢每个试图提供帮助的人。我不是在寻找最漂亮的解决方案(我不得不将这个奖项授予我自己的版本,仅仅是因为它简短且易于阅读和维护:)

最佳答案

关于事件需要了解的一件重要事情是,它们会导致 Hook 到事件的对象不被垃圾回收,直到事件所有者被垃圾回收,或者直到事件处理程序被解除 Hook 。

举个例子,如果你有一个有许多神的多神教万神殿,你可以在其中提升和降级神,例如

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

神将在附加到 Believer.Prayed 时保留在您的 RAM 中。这会导致您的应用程序泄漏神。


我也会对设计决策发表评论,但我知道您制作的示例可能不是真实场景的最佳副本。

在我看来,不创建从 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 方法或将神保存在 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));
}
}

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

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

为什么?因为这样您就可以更精细地控制执行祈祷 Action 。如果您最终决定需要将 Believer 子类化为一种在特定日子不祈祷的特殊类型,那么您就可以控制它。

关于C# - 非静态类上的静态事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21778766/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com