Это проще всего понять, посмотрев код, сгенерированный компилятором, который похож на:
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);
}
}