事件处理程序会阻止垃圾收集吗?

206
如果我有以下代码:
MyClass pClass = new MyClass();
pClass.MyEvent += MyFunction;
pClass = null;

pClass会被垃圾回收吗?还是它会一直挂在那里,每当事件发生时都会触发它们?我需要按照以下方式才能允许垃圾回收吗?

MyClass pClass = new MyClass();
pClass.MyEvent += MyFunction;
pClass.MyEvent -= MyFunction;
pClass = null;

11
对于对这个问题感兴趣的读者,我暂时建议他们熟悉轻量级事件/弱事件模式,这种模式不会防止垃圾回收的发生。一个很好的开始是访问这个主题的stackoverflow页面:https://dev59.com/6HVC5IYBdhLWcg3wykQt - fostandy
21
留给后人的注释:将引用设为null只是通过扩展引用的范围延迟垃圾回收器的执行。.NET与VB6不同。 - John Saunders
4个回答

245
对于具体问题“pClass是否会被垃圾回收”,事件订阅对pClass(作为发布者)的回收没有影响。
对于一般的GC(特别是目标上):这取决于MyFunction是静态的还是基于实例的。
委托(例如事件订阅)到一个实例方法包括对该实例的引用。因此,是的,事件订阅将防止GC。但是,一旦发布事件的对象(pClass 上面)有资格进行收集时,这个问题就不再存在了。
请注意,这是单向的; 如果我们有:
publisher.SomeEvent += target.SomeHandler;

那么,“发布者”将使“目标”保持活动状态,但“目标”将不会使“发布者”保持活动状态。

因此,如果 pClass 将被收集,那么无需取消订阅侦听器。但是,如果 pClass 的寿命很长(比具有 MyFunction 实例的实例更长),则 pClass 可以使该实例保持活动状态,因此如果您希望收集目标,则需要取消订阅。

但是,静态事件在使用基于实例的处理程序时非常危险。


6
如果问题是“pClass会被垃圾回收吗”,那么答案“这取决于......”实际上并不正确。正如Marc在下文中指出的那样,它并不取决于任何事情。 - Tor Haugen
@Tor - 好的,我会澄清。 - Marc Gravell
尽管事件订阅委托只指向一条路,但是当订阅者完成后有任何取消订阅事件的意图时,将需要某种形式的对发布者的引用。它可以是一个“弱引用”,在某些情况下这可能是一个好主意,但通常情况下它将是一个强引用。 - supercat
这是一个很好的答案,因为它还回答了问题的另一半(没有被问到的部分):_发布者_将阻止_GC_对订阅者进行垃圾回收。 - Bob Sammers
是的,正如@BobSammers所说,如果一个短寿命的实例(比如Form/Window)订阅了一个长寿命的服务(比如提供数据的Singleton),那么这可能真的会成为一个问题:Singleton会保留引用,因此即使我们认为它们已经被卸载,对象仍然会被保留在内存中!因此,在使用事件时要非常谨慎。我们在我们的大型软件中滥用了事件,后来解决起来非常困难。 - Elo

11

是的,pClass 将被垃圾回收。事件订阅并不意味着存在任何对 pClass 的引用。

因此,您无需分离处理程序以使 pClass 被垃圾回收。


8

当一个内存单元不再被引用时,它就成为垃圾回收的候选对象。当你的类实例超出作用域时,它不再被程序引用。它不再使用,因此可以安全地进行回收。

如果你不确定某个对象是否会被回收,请问自己以下问题:它是否仍然有引用存在?事件处理程序是由对象实例引用的,而不是相反。


0

pClass 将被垃圾回收。然而,如果上述代码片段位于另一个类中,则该类的实例可能不会被清除,如果您不将 pClass 设置为 null


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接