重新分配一个对象会导致旧对象和附加到它的所有匿名事件处理程序的垃圾回收吗?
问题的原因是作者最初误读了问题,以为是订阅者而不是发布者超出范围。如果事件发布者超出范围,那么订阅者的引用(当然不是订阅者本身!)也会一起随之消失,不需要显式地删除它们。
原始答案是关于如果创建一个事件订阅者并让它超出范围而不取消订阅会发生什么。这与你的问题无关,但我会保留它以供参考。
如果通过事件处理程序仍然注册了类,则它仍然是可达的。它仍然是一个活动对象。遵循事件图的GC将找到与之相连的对象。是的,你需要显式地删除事件处理程序。
仅仅因为对象超出了其原始分配的范围,并不意味着它成为GC的候选对象。只要存在活动引用,它就是活动的。
我不认为在这里需要取消订阅- GC看到事件发布者的引用,而不是它自己,这里我们关心的是发布者。
Skeet:你是对的。我最初误读了问题。我已经更正了我的回答以反映现实情况。
重分配对象会导致旧对象和附加到其上的所有匿名事件处理程序的垃圾回收。这个问题的出现原因是,在某些情况下,事件发布者是长期存在的,但订阅者不希望存在。在这种情况下,需要取消订阅处理程序来避免潜在的内存泄漏。解决方法是在不再需要订阅时手动取消订阅事件处理程序。
以下是一个示例代码,展示了取消订阅事件处理程序的工作原理:
using System; public class Publisher { public event EventHandler Foo; public void RaiseFoo() { Console.WriteLine("Raising Foo"); EventHandler handler = Foo; if (handler != null) { handler(this, EventArgs.Empty); } else { Console.WriteLine("No handlers"); } } } public class Subscriber { public void FooHandler(object sender, EventArgs e) { Console.WriteLine("Subscriber.FooHandler()"); } } public class Test { static void Main() { Publisher publisher = new Publisher(); Subscriber subscriber = new Subscriber(); publisher.Foo += subscriber.FooHandler; publisher.RaiseFoo(); publisher.Foo -= subscriber.FooHandler; publisher.RaiseFoo(); } }
结果:
Raising Foo Subscriber.FooHandler() Raising Foo No handlers
这个示例展示了如何取消订阅事件处理程序,当取消订阅后再次触发事件时,不会调用事件处理程序。
总结起来,如果事件发布者是长期存在的,但订阅者不想存在,就需要手动取消订阅事件处理程序,以避免内存泄漏。