Шаблоны слабых событий
В приложениях возможно, что обработчики, присоединенные к источникам событий, не будут уничтожены в соответствии с объектом прослушиватель, который присоединил обработчик к источнику. Это может привести к утечке памяти. Windows Presentation Foundation (WPF) представляет шаблон, который может использоваться для решения этой проблемы путем предоставления выделенного класса диспетчера для конкретных событий и реализации интерфейса прослушивателей для данного события. Этот шаблон разработки называется шаблоне слабых событий.
Почему реализация шаблона слабых событий?
Прослушивание событий может привести к утечке памяти. Обычным методом для прослушивания событий — использовать синтаксис конкретного языка, который присоединяет обработчик к событию на источнике. Например, в C#, что синтаксис является: source.SomeEvent += new SomeEventHandler(MyEventHandler)
.
Этот метод создает строгую ссылку из источника события для прослушивателя событий. Обычно присоединение обработчика события для прослушивателя вызывает прослушиватель для времени жизни объекта, зависит от времени существования объекта источника (если не будет явно удален обработчик событий). Но в некоторых случаях вы можете время жизни объекта прослушивателя управляются другими факторами, например, принадлежит ли он в настоящее время к визуальному дереву приложения, а не по времени существования источника. Каждый раз, когда время жизни объекта источника выходит за пределы время жизни объекта-прослушивателя, обычный шаблон события приводит к утечке памяти: прослушиватель хранится дольше, чем планировалось.
Шаблон слабых событий предназначен для решения проблемы утечки памяти. Шаблон слабых событий можно использовать всякий раз, когда прослушиватель должен зарегистрироваться для получения события, но прослушиватель не знает, явно отмены регистрации. Шаблон слабых событий можно также использоваться всякий раз, когда время жизни объекта-источника превышает время существования полезных объект прослушивателя. (В этом случае полезные определяется пользователем.) Шаблон слабых событий позволяет прослушивателя, чтобы зарегистрироваться и получить событие, не затрагивая характеристики времени жизни объекта прослушивателя любым способом. По сути неявная ссылка из источника не определяет, является ли прослушиватель под сбор мусора. Ссылка является слабой ссылки, таким образом система именования шаблона слабых событий и в сопутствующих API. Прослушиватель может быть собран как мусор или в противном случае уничтожения, а источник может продолжить без сохранения только что уничтоженный обработчика ссылок на объект.
Кто должен реализовывать шаблон слабых событий?
Реализация шаблона слабых событий представляет интерес в первую очередь для разработчиков элементов управления. Как разработчик элемента управления — во многом за поведение и включения элемента управления и его влияние на приложения, в которых она вставляется. Это включает и поведения времени существования объекта элемента управления, в частности, обработка описанной проблемы утечки памяти.
Некоторые сценарии изначально подходят для применения этого шаблона слабых событий. Одним из таких сценариев является привязка данных. В привязке данных, довольно часто для исходного объекта, была полностью независима от объект-прослушиватель, который является целевым объектом привязки. Многие аспекты WPF привязки данных уже имеют шаблон слабых событий, примененный в порядок реализации событий.
Реализация шаблона слабых событий
Реализация шаблона слабых событий тремя способами. В следующей таблице перечислены три подхода и приводятся рекомендации по при их использованию.
Подход | Когда следует реализовать |
---|---|
Использовать существующий класс manager слабых событий | Если вы хотите подписаться на событие имеет соответствующий WeakEventManager, с помощью существующего диспетчера слабых событий. Список диспетчеров слабых событий, которые входят в состав WPF, см. в разделе иерархии наследования в WeakEventManager класса. Так, как диспетчеры включены слабых событий ограничены, возможно, необходимо будет выбрать один из других подходов. |
Использовать класс manager универсального слабых событий | Используйте универсальный WeakEventManager<TEventSource,TEventArgs> существующей WeakEventManager не доступен, требуется, чтобы легко реализовать, и вы не занимается увеличивает производительность работы. Универсальный WeakEventManager<TEventSource,TEventArgs> является менее эффективным, чем диспетчер слабых событий существующих или пользовательских. Например универсальный класс выполняет дополнительные отражение для обнаружения событий, имени события. Кроме того, код для регистрации событий с помощью универсального WeakEventManager<TEventSource,TEventArgs> является более подробным по сравнению с помощью существующего или пользовательских WeakEventManager. |
Создайте класс manager пользовательских слабых событий | Создание пользовательского WeakEventManager существующей WeakEventManager недоступен и нужно, чтобы повышения эффективности работы. С помощью пользовательского WeakEventManager подписаться на событие будет более эффективной, но вы затраты на создавать больше кода в начале. |
С помощью диспетчера сторонних слабых событий | NuGet имеет несколько диспетчеров слабых событий и многие платформы WPF также поддерживать шаблон (например, см. в разделе документации призмы подписка слабо связанных событий). |
В следующих разделах рассматривается реализация шаблона слабых событий. Для целей данного обсуждения Чтобы подписаться на событие имеет следующие характеристики.
Имя события находится
SomeEvent
.Событие
EventSource
класс.Обработчик событий имеет тип:
SomeEventEventHandler
(илиEventHandler<SomeEventEventArgs>
).Событие передает параметр типа
SomeEventEventArgs
обработчикам событий.
С помощью существующего класса слабого диспетчера событий
Найдите событие слабые manager.
Список диспетчеров слабых событий, которые входят в состав WPF, см. в разделе иерархии наследования в WeakEventManager класса.
Использование нового диспетчера слабых событий вместо обычной привязке события.
Например, если ваш код использует следующий шаблон для подписки на событие:
source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
Измените его на следующий шаблон:
SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
Аналогично Если ваш код использует следующий шаблон для отмены подписки на событие:
source.SomeEvent -= new SomeEventEventHandler(OnSome);
Измените его на следующий шаблон:
SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
Использование универсального класса слабого диспетчера событий
Использование универсального WeakEventManager<TEventSource,TEventArgs> класса вместо обычной привязке события.
При использовании WeakEventManager<TEventSource,TEventArgs> Чтобы зарегистрировать прослушивателей событий, необходимо указать источник события и EventArgs тип как параметры типа для класса и вызове AddHandler как показано в следующем коде:
WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);
Создание пользовательского класса слабого диспетчера событий
Скопируйте следующий шаблон класса в проект.
Этот класс наследует от WeakEventManager класса.
class SomeEventWeakEventManager : WeakEventManager { private SomeEventWeakEventManager() { } /// <summary> /// Add a handler for the given source's event. /// </summary> public static void AddHandler(EventSource source, EventHandler<SomeEventEventArgs> handler) { if (source == null) throw new ArgumentNullException("source"); if (handler == null) throw new ArgumentNullException("handler"); CurrentManager.ProtectedAddHandler(source, handler); } /// <summary> /// Remove a handler for the given source's event. /// </summary> public static void RemoveHandler(EventSource source, EventHandler<SomeEventEventArgs> handler) { if (source == null) throw new ArgumentNullException("source"); if (handler == null) throw new ArgumentNullException("handler"); CurrentManager.ProtectedRemoveHandler(source, handler); } /// <summary> /// Get the event manager for the current thread. /// </summary> private static SomeEventWeakEventManager CurrentManager { get { Type managerType = typeof(SomeEventWeakEventManager); SomeEventWeakEventManager manager = (SomeEventWeakEventManager)GetCurrentManager(managerType); // at first use, create and register a new manager if (manager == null) { manager = new SomeEventWeakEventManager(); SetCurrentManager(managerType, manager); } return manager; } } /// <summary> /// Return a new list to hold listeners to the event. /// </summary> protected override ListenerList NewListenerList() { return new ListenerList<SomeEventEventArgs>(); } /// <summary> /// Listen to the given source for the event. /// </summary> protected override void StartListening(object source) { EventSource typedSource = (EventSource)source; typedSource.SomeEvent += new EventHandler<SomeEventEventArgs>(OnSomeEvent); } /// <summary> /// Stop listening to the given source for the event. /// </summary> protected override void StopListening(object source) { EventSource typedSource = (EventSource)source; typedSource.SomeEvent -= new EventHandler<SomeEventEventArgs>(OnSomeEvent); } /// <summary> /// Event handler for the SomeEvent event. /// </summary> void OnSomeEvent(object sender, SomeEventEventArgs e) { DeliverEvent(sender, e); } }
Замените
SomeEventWeakEventManager
с собственным именем.Замените имена трех, описанные ранее, с соответствующими именами для события. (
SomeEvent
,EventSource
, иSomeEventEventArgs
)Настроить видимость (открытый / внутренней / закрытый) для класса manager слабых событий в ту же видимость, что событие, которыми она управляет.
Использование нового диспетчера слабых событий вместо обычной привязке события.
Например, если ваш код использует следующий шаблон для подписки на событие:
source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
Измените его на следующий шаблон:
SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
Аналогично Если ваш код использует следующий шаблон для отмены подписки на события:
source.SomeEvent -= new SomeEventEventHandler(OnSome);
Измените его на следующий шаблон:
SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);