Язык C #: почему WeakReference или Weak Event Pattern? - PullRequest
1 голос
/ 11 июля 2011

Я читаю "Язык 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)

Ответы [ 2 ]

5 голосов
/ 11 июля 2011

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

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

Не совсем. Это не неожиданное поведение. Ожидается, что объект будет вызван, если вы не отмените его явную регистрацию.

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

Если вам нужно сделать позже, либо используйте шаблон IDisposable и конструкцию «using» для подписчиков, либо сделайте явную отмену подписки.

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

Слабые события не связаны с автоматической отменой подписки даже в тот момент, когда объект выходит из области видимости.

1 голос
/ 30 июля 2011

Если подписчик и издатель события сотрудничают, можно реализовать разумный шаблон слабых событий в .net без необходимости отражения или других приемов CLR.Для подписчика событий было бы возможно реализовать шаблон слабых событий в одностороннем порядке, если бы для правильной работы методов отмены подписки при вызове потоком финализатора требовалось, но, к сожалению, такое ожидание нецелесообразно при подписке на события из неизвестного класса (например, INotifyPropertyChanged).Хитрость заключается в том, чтобы любой, кто действительно «заинтересован» в объекте, содержал сильную ссылку на обертку, а обработчик событий и другие вещи - в ссылку на «внутренности» объекта.Оболочка может содержать ссылку как на кишки, так и на объект с помощью метода Finalize, который отменяет подписку на событие.

...