为什么C#中事件的实现默认不使用弱事件模式?

Why is the implementation of events in C# not using a weak event pattern by default?

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

中的事件模式使订阅者保持活动状态,只要事件的发布者处于活动状态。因此,如果你不取消订阅,你就会泄漏内存(好吧,不是真的泄漏 - 但内存仍然被不必要地占用)。

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

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

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

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

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

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

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

我个人很少发现事件的强引用性是个问题。通常,事件连接在具有相同或非常相似生命周期的对象之间。例如,您可以在 ASP.NET 中的 HTTP 请求上下文中连接所有您想要的事件,因为当请求结束时 所有内容 都将符合收集条件。任何泄漏的大小和寿命都是有限的。