Может ли использование лямбда-выражений в качестве обработчиков событий вызвать утечку памяти?

StackOverflow https://stackoverflow.com/questions/16473

Вопрос

Скажем, у нас есть следующий метод:

private MyObject foo = new MyObject();

// and later in the class

public void PotentialMemoryLeaker(){
  int firedCount = 0;
  foo.AnEvent += (o,e) => { firedCount++;Console.Write(firedCount);};
  foo.MethodThatFiresAnEvent();
}

Если создается экземпляр класса с этим методом и PotentialMemoryLeaker метод вызывается несколько раз, возникает ли утечка памяти?

Есть ли способ отсоединить этот обработчик лямбда-событий после того, как мы закончим вызов? MethodThatFiresAnEvent?

Это было полезно?

Решение

Да, сохраните его в переменную и отцепите.

DelegateType evt = (o, e) => { firedCount++; Console.Write(firedCount); };
foo.AnEvent += evt;
foo.MethodThatFiresAnEvent();
foo.AnEvent -= evt;

И да, если вы этого не сделаете, вы утечка памяти, так как вы каждый раз будете подключать новый объект делегата.Вы также заметите это, потому что каждый раз, когда вы вызываете этот метод, он выводит на консоль все большее количество строк (не просто увеличивающееся количество, но за один вызов MethodThatFiresAnEvent он выгружает любое количество элементов, один раз за каждый подключил анонимный метод).

Другие советы

Вы не просто потеряете память, вы также будете вызывать свою лямбду несколько раз.Каждый вызов PotentialMemoryLeaker добавит еще одну копию лямбды в список событий, и каждая копия будет вызываться при запуске AnEvent.

Ну, вы можете расширить то, что было сделано здесь сделать делегатов более безопасными в использовании (без утечек памяти)

Ваш пример просто компилируется в частный внутренний класс с именем компилятора (с полем firedCount и методом с именем компилятора).Каждый вызов PotentialMemoryLeaker создает новый экземпляр класса замыкания, на который foo сохраняет ссылку посредством делегата на единственный метод.

Если вы не ссылаетесь на весь объект, которому принадлежит PotentialMemoryLeaker, то весь этот объект будет собран мусором.В противном случае вы можете либо установить фу обнулить или очистить список обработчиков событий foo, написав следующее:

foreach (var handler in AnEvent.GetInvocationList()) AnEvent -= handler;

Конечно, вам понадобится доступ к МойОбъект частные члены класса.

Да, точно так же, как обычные обработчики событий могут вызывать утечки.Потому что лямбда фактически меняется на:

someobject.SomeEvent += () => ...;
someobject.SomeEvent += delegate () {
    ...
};

// unhook
Action del = () => ...;
someobject.SomeEvent += del;
someobject.SomeEvent -= del;

По сути, это всего лишь сокращение того, что мы использовали в версии 2.0 все эти годы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top