Подтвердить что ты не робот

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

Я использую несколько UserControls, которые создаются и уничтожаются в моем приложении во время выполнения (путем создания и закрытия подокнов с этими элементами управления внутри).
Это WPF UserControl и наследуется от System.Windows.Controls.UserControl. Нет метода Dispose(), который я мог бы переопределить.
PPMM - это Singleton с тем же временем жизни, что и мое приложение.
Теперь в конструкторе моего (WPF) UserControl я добавляю обработчик событий:

public MyControl()
{
    InitializeComponent();

    // hook up to an event
    PPMM.FactorChanged += new ppmmEventHandler(PPMM_FactorChanged);
}

Я привык к удалению такого обработчика событий в деструкторе:

~MyControl()
{
    // hook off of the event
    PPMM.FactorChanged -= new ppmmEventHandler(PPMM_FactorChanged);
}

Сегодня я наткнулся на это и подумал:

1) Это необходимо? Или GC заботится об этом?

2) Это работает? Или мне нужно сохранить вновь созданный ppmmEventHandler?

Я с нетерпением жду ваших ответов.

4b9b3361

Ответ 1

Так как PPMM - долгоживущий объект (singleton), то этот код не имеет большого смысла.

Проблема заключается в том, что до тех пор, пока этот обработчик событий ссылается на объект, он не будет иметь права на сборку мусора, так как в течение всего оставшегося объекта, который владеет этим событием, жив.

Таким образом, размещение чего-либо в деструкторе бессмысленно, либо:

  • Обработчик событий уже удален, поэтому объект стал доступен для сбора мусора
  • Обработчик событий не удаляется, объект-владелец не имеет права на сбор мусора, и поэтому финализатор никогда не получит вызов
  • Оба объекта имеют право на сбор мусора, и в этом случае вы не должны иметь доступ к этому другому объекту вообще в финализаторе, так как вы не знаете его внутреннего состояния

Короче говоря, не делать этого.

Теперь можно добавить другой аргумент о добавлении такого кода в метод Dispose, когда вы реализуете IDisposable. В этом случае он полностью имеет смысл, так как его пользовательский код, который вызывает Dispose, в предопределенной и контролируемой точке.

Финализатор (деструктор), однако, вызывается только тогда, когда объект имеет право на сбор мусора и имеет финализатор, и в этом случае нет точки.

Что касается вопроса nbr. 2, который я беру как "Могу ли я отказаться от подписки на подобные события", тогда да, вы можете. Единственный раз, когда вам нужно удержать делегата, с которым вы подписались, - это когда вы создаете делегат вокруг анонимного метода или выражения лямбда. Когда вы создадите его вокруг существующего метода, он будет работать.


Изменить: WPF. правильно, не видел этого тега. Извините, остальная часть моего ответа не имеет большого смысла для WPF, и поскольку я не WPF-гуру, я не могу сказать. Однако есть способ исправить это. Это совершенно законно здесь, на SO, чтобы размахивать контентом другого ответа, если вы можете его улучшить. Поэтому, если кто-нибудь знает, как правильно это сделать с помощью пользовательского контроля WPF, вы можете снять весь первый раздел моего ответа и добавить соответствующие бит WPF.

Изменить. Позвольте мне также ответить на вопрос в комментарии здесь.

Поскольку рассматриваемый класс является пользовательским, его время жизни будет привязано к форме. Когда форма закрывается, она будет распоряжаться всеми дочерними элементами управления, которыми она владеет, другими словами, уже существует метод Dispose.

Правильный способ управления данным пользователем, если он управляет своими собственными событиями, заключается в том, чтобы отцепить обработчики событий в методе Dispose.

(оставлено удалено) Забастовкa >

Ответ 2

Сначала я бы сказал, что не используйте деструктор, но Dispose() для очистки ваших ресурсов.

Во-вторых, по моему мнению, если этот код находится внутри объекта, который создается очень часто и имеет короткий срок службы, лучше, если вы позаботитесь об удалении обработчика событий, поскольку это ссылка на объект-держатель, что предотвратит GC, чтобы собрать его.

С уважением.

Ответ 3

WPF не поддерживает IDisposable. Если вы используете элемент управления WPF, который нуждается в очистке, вы должны подумать о подключении к Loaded и Unloaded вместо этого (или дополнительно).

т.е. вы подключаетесь к событию в обработчике Loaded и отсоединяетесь в обработчике Unloaded. Конечно, это только вариант, если ваш элемент управления не должен получать событие, пока он не "загружен", и если вы можете правильно поддерживать многие циклы загрузки/выгрузки.

Преимущество использования событий Loaded/Unloaded заключается в том, что вам не нужно вручную удалять пользовательский элемент управления везде, где он использовался. Однако вы должны знать, что событие Unloaded не запускается после того, как завершение работы приложения завершено. Например. если ваш режим выключения OnMainWindowClose, события Unloaded для других окон не будут запущены. Однако это обычно не проблема. Это просто означает, что вы не можете надежно выполнить команду в Unloaded, которая должна произойти до того, как приложение завершится.

Ответ 4

Если код попал в деструктор, это уже не имеет значения.

Это потому, что он будет уничтожен только в том случае, если он больше не будет слушать какие-либо события. Если бы он все еще слушал события, это не было бы уничтожено.

Ответ 5

Является ли PPMM чем-то внешним с более длительным временем жизни, чем MyControl экземпляры?

Если это так, если PPMM_FactorChanged не является статическим методом, ppmmEventHandler будет ссылаться на экземпляр MyControl live - это означает, что экземпляр никогда не будет иметь права на сбор мусора, и финализатор никогда не будет стрелять.

Вам не нужно поддерживать ppmmEventHandler для кода удаления.

Ответ 6

ГК позаботится об этом. Хотя событие содержит сильную ссылку, оно удерживает его только на родительском объекте. В конце концов, только MyControl сохранит ссылку через обработчик событий, поэтому GC соберет его.

С другой стороны, используя финализатор, который НЕ является descrutor. Для этой плохой практики. Если вы хотите отменить регистрацию события, вы должны рассмотреть IDisposable.

Ответ 7

Обработчики событий сложны и могут легко скрыть утечку ресурсов. Как говорит Тигран. Используйте IDisposeable и забывайте о деструкторах. Я рекомендую измерить, правильно ли вы это сделали. Просто взглянув на потребление памяти вашего приложения в диспетчере задач, вы узнаете, есть ли утечка, если вы немного стресс протестируете, загрузив и закрыв несколько тысяч окон.

Ответ 8

Есть случаи, когда отмена подписки на событие в Finalizer/destructor может быть полезна, если издатель событий гарантировал, что отказ от подписки был потокобезопасным. Поскольку объект для отказа от подписки на свои собственные события был бы бесполезен, но в качестве работоспособного шаблона публичный объект должен содержать ссылку на частный объект, который фактически "выполняет всю работу", и чтобы этот частный объект подписался на события, Если нет ссылки от частного объекта обратно на общедоступный объект, общественный объект станет подходящим для завершения, как только никто его не интересует; его финализатор затем сможет отменить подписку от имени частного объекта.

К сожалению, этот шаблон может работать только в том случае, если объекты, чьи события подписаны, гарантируют, что он может принимать запросы на подписку от любого потока, а не только контекст, на котором были подписаны события. Требование .NET в качестве части "события" заключить, что все методы отмены подписки должны быть потокобезопасными, не налагали бы серьезную нагрузку на реализации, но по каким-либо причинам МС не налагал такого требования. Следовательно, даже если Finalizer/destructor обнаруживает, что событие должно быть отписано как можно скорее, нет стандартного механизма, с помощью которого он может это сделать.

Ответ 9

2) Это работает

1) У меня был случай (с услугой обмена сообщениями In-App), что обработчики событий были глобальным объектом, который не был выпущен, и GC из-за этого не смог собрать объект. Я думаю, что это обычно редкое условие - используя профилировщик, такой как ANTS из красных ворот, вы можете легко выполнить профилирование памяти, если думаете, что это происходит с вами.