这通过查看编译器生成的代码最容易理解,类似于:
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
(not 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);
}
}