这最好通过查看编译器生成的代码来理解,它类似于:
public void AttachToAEvent()
{
_foo.AEvent += new EventHandler(this.Handler);
}
[CompilerGenerated]
private void Handler(object sender, EventArgs e)
{
this.UseBar(this._bar);
}
如显然所示,创建的委托是一个实例委托(针对对象上的实例方法),因此必须持有对该对象实例的引用。
“由于委托捕获了变量this._bar,它是否隐式地保持对B对象实例的引用?”
实际上,匿名方法仅捕获了“this”(而不是“this._bar”)。从生成的代码可以看出,构造的委托确实会持有对B实例的引用。它必须这样做;否则,每当执行委托时,如何按需读取字段呢?请记住,“变量”被捕获,而不是“值”。
“由于在我的情况下,A实例的生存时间比B实例长得多且小得多,我担心这样做会导致“内存泄漏。”
是的,你完全有理由这样认为。只要A实例可访问,B事件订阅者仍将可访问。如果您不想使用弱事件,您需要重写此内容,以便在不再需要处理程序时注销处理程序。
“如果_bar是AttachToAEvent方法的局部变量,情况会有所不同吗?”
是的,它会有所不同,因为捕获的变量将变为bar局部变量,而不是this。但是,假设UseBar是一个实例方法,那么您的“问题”(如果您想这样认为)就变得更糟了。编译器现在需要生成一个事件侦听器,它“记住”局部变量和包含的B对象实例。
这是通过创建一个闭包对象并使其(实际上是它的一个实例方法)成为委托的目标来实现的。
public void AttachToAEvent(int _bar)
{
Closure closure = new Closure();
closure._bar = _bar;
closure._bInstance = this;
_foo.AEvent += new EventHandler(closure.Handler);
}
[CompilerGenerated]
private sealed class Closure
{
public int _bar;
public B _bInstance;
public void Handler(object sender , EventArgs e)
{
_bInstance.UseBar(this._bar);
}
}