2012-03-02 4 views
5

У меня довольно короткий вопрос о анонимных обработчиков событий:анонимных обработчиков событий и утилизации

Это код, который я имею:

public void AddTestControl(Control ctrl) 
{ 
    ctrl.Disposed += (o, e) => { RemoveTestControl(ctrl); }; 
    ctrl.SomeEvent += _Control_SomeEvent; 
} 

public void RemoveTestControl(Control ctrl) 
{ 
    ctrl.SomeEvent -= _Control_SomeEvent; 
} 

Является ли этот код выше штраф, или если код быть перезаписаны, чтобы удалить Обработчик Обработанных Событий? Что-то вроде этого:

public void AddTestControl(Control ctrl) 
{ 
    ctrl.Disposed += _Control_Disposed; 
    ctrl.SomeEvent += _Control_SomeEvent; 
} 

public void RemoveTestControl(Control ctrl) 
{ 
    ctrl.Disposed -= _Control_Disposed; 
    ctrl.SomeEvent -= _Control_SomeEvent; 
} 

ответ

8

В общем, единственная ситуация, когда вам необходимо удалить обработчики событий из объекта для того, чтобы иметь право на сборку мусора, когда издатель объекта (один Определяя событие) живет дольше, чем абонент (тот, который содержит обработчики событий). В такой ситуации GC не сможет выпустить абонента, когда он выходит из сферы действия, поскольку он все еще ссылается издателем.

В этом случае, если предположить, что это WebForms или WinForms, издатель (то есть Control объект), скорее всего, это дочерний объект абонента (вероятно, Page или Form), который будет первым чтобы выйти из области действия, связав с ней все связанные с ней объекты. Следовательно, есть нет необходимости удалять обработчики событий.

+0

afaik nobody обещает выполнение порядка обработчиков событий. – b0rg

+0

Но мой вопрос больше касается события Disposed. Нужно ли мне самостоятельно отсоединять его или это первый пример кода? – juFo

+1

@juFo Предполагая, что причина, о которой вы просите, связана с тем, что вы беспокоитесь о утечке памяти, тогда ** нет, вам не нужно самостоятельно отсоединять обработчики событий **. Поскольку объект 'Control' имеет одинаковое время жизни' Page' или 'Form', содержащее обработчики событий, они будут выходить из области действия вместе. –

2

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

Затем снова, сколько людей отправляется в проблему отказа от подписки на каждый обработчик событий, например. приложение WinForms? Объектные ссылки указывают от издателя на подписчиков, а не наоборот, поэтому издатель может быть собран, пока подписчики живут. Он не представляет ту же опасность, что и ситуация , противоположная ситуации, где долгоживущий издатель (например, статическое событие) может расточительно удерживать потенциально крупных подписчиков в живых после того, как их можно было собрать.

Если вам нужно/нужно отказаться от подписки, тогда требование отказаться от подписки того же делегата делает анонимные обработчики событий немного больными. Reactive Extensions решает эту проблему аккуратным способом: вместо того, чтобы помнить делегата, которого вы подписали, подписка возвращает IDisposable, который отписывается при размещении. Выбирая все ваши подписки в CompositeDisposable, вы можете отказаться от подписки всего одним звонком Dispose.

1

Оба кодекса в порядке, но я предпочитаю второй вопрос личного характера. Он читается более четко, чем первый.

Наверху с первым кодом есть анонимный делегат лямбда, и он получает текущую ссылку на ctrl. Этот код может вести себя непредсказуемо в зависимости от ситуации и настроек оптимизации компиляции: независимо от того, включен ли вызов или нет.

Не говоря уже о том, что с кодом есть архитектурная проблема: у вас есть ControlOwner и куча Child Controls.Я полагаю, вы добавляете дочерние элементы управления ControlOwner во время выполнения, а затем пытаетесь реагировать на их поведение, подписываясь на ControlOwner на события childControl. Это отлично подходит для таких событий, как _ButtonClicked и т. Д. Но не подходит для Dispose. Пусть дочерний элемент управления справится с этим, сам OwnerControl не должен знать об этом. Не говоря уже о том, что он может не существовать одновременно с ChildControl [n] .Dispose.

Короче говоря: * лучше оставить выбрасывайте событие на ChildControl самостоятельно и делать все убирать в ChildControl.Dispose * не нужно отказаться от событий. Диспетчер событий проверяет, жив ли абонент.

Смежные вопросы