【问题标题】:Why is the implementation of events in C# not using a weak event pattern by default?为什么 C# 中事件的实现默认不使用弱事件模式?
【发布时间】:2015-03-15 18:43:48
【问题描述】:

这个问题可能会导致推测性的答案,但我认为在 中实现event 背后有一个深思熟虑的设计决策。

只要事件的发布者还活着, 中的事件模式就会使订阅者保持活跃。因此,如果您不取消订阅,您就是在泄漏内存(嗯,不是真的泄漏 - 但内存仍然被不必要地占用)。

如果我想阻止这种情况,我可以取消订阅事件或将弱事件模式实现为proposed at MSDN

由于事件模式导致了这么多问题(对于初学者?),问题是:为什么决定发布者保留对订阅者的强引用,而不是让它们独立或允许开发者明确地拥有 @ 987654326@ 或 weak 修饰符?

这里已经有几个questions 关于这个话题,答案听起来很合理,但没有人真正回答为什么会这样。

【问题讨论】:

  • 因为在.NET 1.0中引入的事件和Winforms(开始时的主要用户),在一个古老的时代,然后它们不能在不破坏一切的情况下进行修改?如果我必须选择一个如果在 1.0 中引入会使 .NET 成为更好的框架的功能,那么泛型和可空值就是该功能。
  • 真的给初学者带来这么多问题吗?你有证据或文件吗?
  • 大多数情况下它是一个红鲱鱼。无论如何,事件都是一种混乱的依赖模式,当你明智地使用时,不会出现泄漏问题。
  • @HenkHolterman 没有研究,但从这里以及网络上围绕该主题的教程和问题的数量来看,我认为这是一个问题。
  • “因此,如果您不取消订阅,则会泄漏内存” - 如果订阅者的寿命比订阅者长。根据我的经验,这种情况并不常见。我曾经被这个咬过,正好一次,但仅此而已。

标签: c# c# c# .net events weak-references


【解决方案1】:

其中一个原因当然是性能。 GC 句柄(为所有“外来”引用提供支持,例如 WeakReference)会带来性能成本。弱事件比“强”事件慢,因为它们需要 GC 句柄。强事件(默认情况下)由存储委托的实例字段实现。这只是一个普通的托管引用,与任何其他引用一样便宜。

事件应该是一种非常通用的机制。它们不仅仅适用于您可能有几十个事件处理程序的 UI 场景。将大量的复杂性和性能成本融入到这样一个基本的语言特性中并不是一个明智的想法。

还有由弱引用引起的语义差异不确定性。如果您将() => LaunchMissiles() 连接到某个事件,您可能会发现有时会发射导弹。其他时候 GC 已经带走了处理程序。这可以通过dependent handles 解决,这会引入另一个级别的复杂性。

请注意,您可以自己对订阅者透明地实现弱事件。从某种意义上说,事件就像属性一样,它们只是基于 addremove 访问器方法的元数据和约定。所以这(只是)一个关于 .NET 语言选择的默认值的问题。这不是 CLR 的设计问题。

我个人认为事件的强引用性质是一个问题的情况很少见。通常,事件被连接在具有相同或非常相似生命周期的对象之间。例如,您可以在 ASP.NET 中的 HTTP 请求上下文中连接所有您想要的事件,因为当请求结束时,一切都将有资格被收集。任何泄漏的大小都是有限的并且是短暂的。

【讨论】:

  • 我知道我可以自己实现弱版本;我只是想知道为什么它不是默认值。导弹的例子非常有道理,是一个很好的例子,为什么强引用是一件好事并且是一个合理的决定。
  • @krumelur 实际上,当它看到匿名委托是订阅者时,没有什么可以让编译器使用强引用。至于性能成本,我相信那些聪明人能够做出好的决定。实际上,一些第 3 方解决方案成功地减少了性能损失(并且还解决了匿名代表的“问题”)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多