Шаблоны WeakEvent
Обновлен: Ноябрь 2007
В обычных приложениях существует возможность того, что обработчики, присоединенные к источнику событий, не будут уничтожены в соответствии с объектом-слушателем, который присоединил обработчик к источнику. Эта ситуация может привести к утечке памяти. Windows Presentation Foundation (WPF) представляет шаблон, который можно использовать для решения этой проблемы путем предоставления выделенного класса диспетчера для конкретных событий и реализации интерфейса слушателей данного события. Этот шаблон разработки называется шаблоном WeakEvent.
Почему следует реализовать шаблон WeakEvent?
Ожидание событий может привести к утечке памяти. Обычным методом для прослушивания событий является использование синтаксиса конкретного языка, который присоединяет обработчик к событию на источнике. Например, в C#, такой синтаксис имеет вид: source.SomeEvent += new SomeEventHandler(MyEventHandler).
Этот метод создает строгую ссылку от источника событий к слушателю событий. Обычно присоединение обработчика событий к слушателю служит причиной того, что слушатель имеет время жизни объекта, на которое влияет время жизни объекта источника (если обработчик событий не удаляется явным образом). Однако, в некоторых случаях может требоваться управление временем жизни объекта-слушателя другими способами, как если бы он в данный момент принадлежал визуальному дереву приложения, а не временем жизни источника. Всякий раз, когда время жизни объекта-источника превосходит время жизни объекта-слушатель, обычный шаблон события приводит к утечке памяти: слушатель хранится дольше, чем ожидается.
Шаблон WeakEvent предназначен для решения проблемы утечки памяти. Шаблон WeakEvent может использоваться, если слушателю необходимо зарегистрировать событие, а момент отмены регистрации явно неизвестен, или если время жизни объекта-источника превышает «полезное» время жизни объекта-слушателя. (Понятие «полезности» определяется разработчиком самостоятельно.) Шаблон WeakEvent позволяет слушателю регистрировать и получать события, никак не затрагивая характеристики времени жизни объекта слушателя. В результате неявная ссылка от источника не играет роли при решении того, подходит ли слушатель для сборки мусора. Эта ссылка является слабой, поскольку является именованием шаблона WeakEvent и связана с API-интерфейсы. Слушатель может быть собран как мусор или, в противном случае, уничтожен, а источник может продолжить свое существование без сохранения не входящих в коллекцию ссылок ресурса на только что уничтоженный объект.
Кто должен реализовывать шаблон WeakEvent?
Реализация шаблона WeakEvent будет интересна главным образом для авторов элементов управления. Это происходит потому, что как автор элемента управления вы в значительной мере отвечаете за поведение и включения элемента управления и за то влияние, которое он оказывает на приложения, в которые он вставлен. В это входит и поведение времени жизни объекта элемента управления, в частности, обработка описанной проблемы утечки памяти.
Некоторые сценарии изначально предоставляются для приложения шаблона WeakEvent. Одним из таких случаев является привязка данных, при которой исходный объект, являющийся объектом данных, зачастую совершенно не зависит от объекта-слушателя, являющегося целью привязки. Многие аспекты привязки данных WPF уже имеют шаблон WeakEvent, примененный тем же способом, что и реализация событий.
Как реализовать шаблон WeakEvent
Реализация шаблона WeakEvent состоит из трех аспектов:
Получение производного диспетчера из класса WeakEventManager.
Реализация интерфейса IWeakEventListener для любого класса, которому требуется зарегистрировать слушателей для слабого события без генерации строгой ссылки на источник.
При регистрации слушателей не следует использовать обычные методы доступа событий добавления и удаления, в которых слушатель должен использовать шаблон. Вместо этого следует использовать реализации «AddListener» и «RemoveListener» в выделенном WeakEventManager для этого события.
WeakEventManager
Обычно создаются управляющие классы со связью 1:1 с событиями, реализующими шаблон. Например, если имеется событие Spin, то будут получен производный класс SpinEventManager в качестве диспетчера выделенного слабого события. Если событие существовало в нескольких исходных классах, функционировало одинаковым образом в каждом классе и использовало общий тип данных события, то один и тот же диспетчер может использоваться для всех классов.
Контрольный список реализации для класса, производного от WeakEventManager, состоит из переопределения двух виртуальных методов и предоставления нескольких других членов, имена которых особым образом не управляются виртуальным шаблоном, но тем не менее должны существовать. Переопределения используются для запуска или завершения режима доставки события инфраструктурой WPF. Другие элементы необходимы для обеспечения функциональных возможностей, чтобы собственные реализации IWeakEventListener могли использовать WeakEventManager для присоединения слушателей к событию.
Подробное описание реализации классов, производных от WeakEventManager, содержится в «Примечаниях к наследующим методам» в разделе справки по WeakEventManager.
IWeakEventListener
Класс реализации IWeakEventListener имеет только одно обязательство: внедрение метода интерфейса ReceiveWeakEvent. Реализация ReceiveWeakEvent должна быть централизованной реализацией, которая направляет все ссылки событий, размещенных в этом классе, к соответствующему WeakEventManager.
Подробная информация по реализации интерфейса IWeakEventListener содержится в «Примечаниях к реализующим методам» в разделе справки по методу ReceiveWeakEvent.
Присоединение слушателей
Предположим, что имеется стандартное событие ClockwiseSpin (определенное через Spinner). Чтобы использовать шаблон для этого события, нужно либо использовать существующий класс ClockwiseSpinEventManager, производный от WeakEventManager, либо реализовать его самостоятельно. Если имеется класс-слушатель SpinListener, который желает быть слушателем, обычная методика присоединения обработчика (без использования шаблона) будет использовать синтаксис +=:
spinnerInstance.ClockwiseSpin += new EventHandler(MyOnCWSpinHandler);
Но если имеется класс, реализующий IWeakEventListener и учетные записи для события ClockwiseSpin и диспетчера в реализации, то для шаблона WeakEvent используется следующий синтаксис:
ClockwiseSpinEventManager.AddListener(spinnerInstance, this);
Тогда логика обработки этого события задается в одном из случаев реализации ReceiveWeakEvent в классе (в отличие от обычного обработчика, основанного на делегате).
Реализация шаблона для внешних событий
Интересным аспектом шаблона WeakEvent является то, что можно реализовать шаблон события, которое не является частью базы кода. С точки зрения источника, способ подключения обработчиков к событию не отличается и управляется через WeakEventManager. Требуется только определить WeakEventManager для этого события, а затем представить это событие как часть логики ReceiveWeakEvent любого предполагаемого слушателя, желающего использовать шаблон для прослушивания данного события.
См. также
Основные понятия
Общие сведения о перенаправленных событиях
Общие сведения о связывании данных