Я читаю "Язык C #", 4-е издание, в нем говорится о WeakReference
и Weak Event Pattern
:
CHRISTIAN NAGEL: утечки памяти часто происходят из-за неправильного использования событий. Если объекты клиента присоединяются к событиям, но не отсоединяются от них, и ссылка на объект клиента больше не используется, объект клиента по-прежнему не может быть восстановлен сборщиком мусора, поскольку ссылка издателя остается. Этого можно избежать путем (1) отсоединения событий, когда клиентский объект больше не используется, (2) пользовательской реализацией методов доступа add
и remove
с использованием класса WeakReference
, содержащего делегат, или (3) Weak Event pattern
, используемый WPF с интерфейсом IWeakEventListener.
У меня есть сомнения: Option "(2) WeakReference
" не приносит никакого удобства вообще, по сравнению с "option (1) отключение событий явно", потому что использование WeakReference
все еще необходимо явно звонит как add
, так и remove
.
В противном случае, даже если один из объектов обработчика событий был назначен на нуль, «потерянный» объект все равно будет реагировать на событие - это вызовет неожиданное поведение.
Примечание: WeakReference
только помогает сборке мусора, так как объекты обработчиков событий не будут затронуты объектами публикации событий; WeakReference
НЕ заставляет объекты-обработчики событий собирать мусор.
Аналогичная проблема применима и к шаблону Weak Event.
Может быть, это немного абстрактно, возьмите образец Посредника Джоша Смита (http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/) в качестве примера.
public class Mediator //...
{
public void Register(object message, Action<object> callback)
{
// notice: Mediator has no Unregister method
}
public void NotifyColleagues(object message, object parameter)
{
// ...
}
}
public class ObjectA //...
{
public string ObjectAText
{
get { return _objectAText; }
set
{
//...
_mediator.NotifyColleagues(MediatorMessages.ObjectASaidSomething, _objectAText);
}
}
}
public class ObjectB //...
{
//...
public ObjectB(Mediator mediator)
{
//...
_mediator.Register(
MediatorMessages.ObjectASaidSomething,
param =>
{
// handling event ObjectASaidSomething
});
}
}
Если у нас есть
ObjectA objectA = new ObjectA();
ObjectB objectB1st = new objectB();
objectA.ObjectAText = "John"; // objectB1st will respond to this event.
objectB1st = null; // due to delay of garbage collection, the object is actually still in memory
ObjectB objectB2nd = new objectB();
objectA.ObjectAText = "Jane"; // both objectB1st and objectB2nd will respond to this event!
Разве последняя строка не вызвала неожиданное поведение из-за WeakReference
?
Но если класс Mediator
предоставляет метод «Unregister» (на самом деле я его реализовал), «option (2) WeakReference
» не будет иметь никакого значения для «option (1) отсоединение событий явным образом». (Сам посредник по-прежнему является полезным шаблоном, который может проникать в иерархию слоев компонентов WPF или MVVM)