Сведения о вопросе

Oleksandrop

01:32, 3rd August, 2020

Теги

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

Просмотров: 452   Ответов: 5

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

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 вызывается несколько раз, происходит ли утечка памяти?

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



  Сведения об ответе

9090

20:16, 22nd August, 2020

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

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

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


  Сведения об ответе

pumpa

12:26, 23rd August, 2020

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


  Сведения об ответе

KOMP

23:02, 27th August, 2020

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


  Сведения об ответе

DAAA

00:33, 18th August, 2020

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

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

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

Конечно, вам понадобится доступ к закрытым членам класса MyObject.


  Сведения об ответе

PROGA

12:59, 26th August, 2020

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

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

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

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться