如果我有如下代码:
public void Foo()
{
Bar bar = new Bar();
bar.SomeEvent += (sender, e) =>
{
//Do something here
};
bar.DoSomeOtherThingAndRaiseSomeEvent();
}
当方法运行超出范围时,bar
是否会被收集,或者我是否需要手动取消对 SomeEvent
的引用以防止内存泄漏?
如果我有如下代码:
public void Foo()
{
Bar bar = new Bar();
bar.SomeEvent += (sender, e) =>
{
//Do something here
};
bar.DoSomeOtherThingAndRaiseSomeEvent();
}
当方法运行超出范围时,bar
是否会被收集,或者我是否需要手动取消对 SomeEvent
的引用以防止内存泄漏?
您的情况没问题;事件订阅者不会阻止发布者被垃圾回收,但反过来可能会发生。
例如,
class Foo
{
public event EventHandler FooEvent;
void LeakMemory()
{
Bar bar = new Bar();
bar.AttachEvent(this);
}
}
class Bar
{
void AttachEvent(Foo foo)
{
foo.FooEvent += (sender, e) => { };
}
}
在这种情况下,在LeakMemory
中创建的Bar
实例只有在以下情况之一发生时才能被回收:
FooEvent
的调用列表中移除这是因为事件(它只是普通delegate
实例上的一些语法糖)保存了一个委托列表,以便在调用事件时调用每个委托。而每个委托又转而引用它所附加到的对象(在这种情况下,是Bar
实例)。
请注意,我们只谈论收集的资格。仅凭其资格并不意味着它会在何时(或甚至是否)被回收,只是说它可以被回收。
public event EventHandler MyEvent;
...
//adds a reference to this named method in the context of the current instance
MyEvent += Foo;
//Adds a reference to this anonymous function literal to MyEvent
MyEvent += (s,e) => Bar();
...
//The named method of the current instance will be the same reference
//as the named method.
MyEvent -= Foo;
//HOWEVER, even though this lambda is semantically equal to the anonymous handler,
//it is a different function literal and therefore a different reference,
//which will not match the anonymous handler.
MyEvent -= (s,e) => Bar();
var hasNoHandlers = MyEvent == null; //false
//To successfully unsubscribe a lambda, you have to keep a reference handy:
EventHandler myHandler = (s,e) => Bar();
MyEvent += myHandler;
...
//the variable holds the same reference we added to the event earlier,
//so THIS call will remove the handler.
MyEvent -= myHandler;
好的,对象bar
不会立即被自动垃圾回收...只是bar
变量不会阻止它被垃圾回收。
事件处理程序也不会阻止Bar
实例被垃圾回收——“正常”的问题是事件处理程序会阻止事件的订阅者被垃圾回收(如果它使用实例方法或在匿名函数中捕获“this”)。通常不会影响发布者被垃圾回收。只需记住,发布者需要保留对所有订阅者的引用——订阅者不需要记住自己订阅了什么,除非它明确想要取消订阅或稍后使用其他成员。
假设没有其他东西使您的Bar
实例保持活动状态,那么您的代码应该没问题。
WeakReference
类的解决方案也只是将问题移动到另一种类型。 - Adam Robinson