Schwache Ereignismuster
In Anwendungen ist es möglich, dass an Ereignisquellen angehängte Handler in Abstimmung mit dem Listenerobjekt, das den Handler an die Quelle angehängt hat, nicht zerstört werden. Diese Situation kann zu Speicherverlusten führen. Windows Presentation Foundation (WPF) führt ein bestimmtes Entwurfsmuster ein, das zum Behandeln dieses Problems eingesetzt werden kann. Hierbei wird eine dedizierte Managerklasse für bestimmte Ereignisse bereitgestellt und eine Schnittstelle auf dem Listener für dieses Ereignis implementiert. Dieses Entwurfsmuster wird als schwaches Ereignismuster bezeichnet.
Gründe zum Implementieren schwacher Ereignismuster
Das Überwachen von Ereignissen kann zu Speicherverlusten führen. Die normalerweise verwendete Methode zum Überwachen eines Ereignisses besteht in der Verwendung der sprachspezifischen Syntax, mit der ein Handler an ein Ereignis in einer Quelle angehängt wird. In C# lautet diese Syntax beispielsweise: source.SomeEvent += new SomeEventHandler(MyEventHandler).
Mit dieser Methode wird ein starker Verweis von der Ereignisquelle zum Ereignislistener erstellt. Normalerweise führt das Anfügen eines Ereignishandlers für einen Listener dazu, dass der Listener eine Objektlebensdauer aufweist, die von der Objektlebensdauer der Quelle beeinflusst wird (es sei denn, der Ereignishandler wird explizit entfernt). In bestimmten Situationen jedoch soll die Objektlebensdauer des Listeners möglicherweise von anderen Faktoren bestimmt werden, wie zum Beispiel der Tatsache, ob er zurzeit zur visuellen Struktur der Anwendung gehört, und nicht von der Lebensdauer der Quelle. Immer dann, wenn die Objektlebensdauer der Quelle die Objektlebensdauer des Listeners überschreitet, führt das normale Ereignismuster zu einem Speicherverlust: Der Listener behält seine Gültigkeit länger als vorgesehen.
Das schwache Ereignismuster ist darauf ausgerichtet, dieses Speicherverlustproblem zu lösen. Das schwache Ereignismuster kann immer dann verwendet werden, wenn sich ein Listener für ein Ereignis registrieren muss. Wann die Registrierung aufzuheben ist wird vom Listener allerdings nicht explizit erkannt. Das schwache Ereignismuster kann auch verwendet werden, wenn die Objektlebensdauer der Quelle die nutzbare Objektlebensdauer des Listeners überschreitet. (In diesem Fall wird nutzbar von Ihnen bestimmt.) Das schwache Ereignismuster ermöglicht dem Listener, sich für das Ereignis zu registrieren und es zu empfangen, ohne dass sich dies auf die Merkmale der Objektlebensdauer des Listeners auswirkt. Der implizite Verweis der Quelle legt nicht fest, ob der Listener für die Garbage Collection freigegeben wird. Der Verweis ist ein schwacher Verweis, woher die Bezeichnung schwaches Ereignismuster und die verwandten APIs rühren. Für den Listener kann eine Garbage Collection durchgeführt werden, oder er kann auf andere Weise zerstört werden, wobei die Quelle weiterhin bestehen bleiben kann, ohne dass Handlerverweise, die nicht aufgelistet werden können, auf das jetzt zerstörte Objekt beibehalten werden.
Wer sollte das schwache Ereignismuster implementieren?
Das Implementieren des schwachen Ereignismusters ist in erster Linie für Autoren von Steuerelementen interessant. Der Steuerelementautor ist im Großen und Ganzen für das Verhalten und die Kapselung des Steuerelements sowie für die Auswirkungen verantwortlich, die das Element auf die Anwendung hat, in die es eingefügt wird. Dies schließt das Objektlebensdauer-Verhalten des Steuerelements ein, insbesondere die Behandlung des beschriebenen Speicherverlustproblems.
Bestimmte Szenarien sind grundsätzlich zur Anwendung des schwachen Ereignismusters sehr gut geeignet. Ein solches Szenario ist z. B. die Datenbindung. Bei der Datenbindung ist das Quellobjekt im Allgemein vollkommen unabhängig vom Listenerobjekt, das ein Ziel einer Bindung ist. In zahlreichen Aspekten der WPF-Datenbindung wird das schwache Ereignismuster bereits durch die Implementierung der Ereignisse angewendet.
So implementieren Sie das schwache Ereignismuster
Das Implementieren des schwachen Ereignismusters umfasst folgende drei Aspekte:
Ableiten eines Managers aus der WeakEventManager-Klasse.
Implementieren der IWeakEventListener-Schnittstelle für jede Klasse, die Listener für das schwache Ereignis registrieren möchte, ohne einen starken Verweis zur Quelle zu erstellen.
Verwenden Sie beim Registrieren der Listener nicht die normalen Accessoren zum Hinzufügen und Entfernen für das Ereignis, für das der Listener das Muster verwenden soll. Verwenden Sie stattdessen die AddListener- und RemoveListener-Implementierungen im dedizierten WeakEventManager-Element für dieses Ereignis.
WeakEventManager
Normalerweise erstellen Sie für das Ereignis eine Managerklasse mit einer 1:1 Beziehung, um das schwache Ereignismuster zu implementieren. Wenn Sie zum Beispiel über ein Ereignis mit der Bezeichnung Spin verfügen, erstellen Sie für das Ereignis eine SpinEventManager-Klasse als dedizierten Manager für das schwache Ereignis. Wenn das Ereignis in mehr als einer Quellklasse vorhanden ist, sich im Großen und Ganzen in jeder Klasse gleich verhält und den gleichen Ereignisdatentyp aufweist, kann der gleiche Manager für jedes Ereignis verwendet werden.
Beim Ableiten von der WeakEventManager-Klasse werden zwei virtuelle Methoden überschrieben und mehrere andere Member verfügbar gemacht, deren Namen nicht ausdrücklich von einer virtuellen Vorlage bestimmt sein, jedoch trotzdem vorhanden sein müssen. Die Überschreibungen dienen dazu, den Ereigniszustellungsmodus durch die WPF-Infrastruktur zu initialisieren oder zu beenden. Die anderen Member stellen Funktionen bereit, damit Ihre eigenen IWeakEventListener-Implementierungen den WeakEventManager zum Anfügen von Listenern an das Ereignis verwenden können.
Weitere Informationen zum Ableiten vom WeakEventManager-Element finden Sie im Abschnitt "Hinweise zur Vererbung" im Referenzthema WeakEventManager.
IWeakEventListener
Die IWeakEventListener-Schnittstelle verfügt über eine einzige Schnittstellenmethode, die ReceiveWeakEvent-Methode. Die ReceiveWeakEvent-Implementierung muss eine zentrale Implementierung sein, die jeden Ereignisverweis, der in dieser Klasse vorhanden ist, an den entsprechenden WeakEventManager weiterleitet.
Weitere Informationen zur Implementierung der IWeakEventListener-Schnittstelle finden Sie im Abschnitt "Hinweise zur Implementierung" im Referenzthema zur ReceiveWeakEvent-Methode.
Anfügen von Listenern
Angenommen, Sie verfügen über ein ClockwiseSpin-Ereignis (definiert durch den Spinner-Typ), bei dem es sich um ein herkömmliches Ereignis handelt. Wenn Sie über eine SpinListener-Listenerklasse verfügen, die als Listener fungieren soll, ist die herkömmliche Methode (ohne Verwendung des Musters) zum Anfügen des Handlers die Verwendung des Operators "+=":
spinnerInstance.ClockwiseSpin += new EventHandler(MyOnCWSpinHandler);
Wenn Sie jedoch über eine Klasse verfügen, die IWeakEventListener implementiert und sich auf das ClockwiseSpin-Ereignis und seinen Manager in der Implementierung bezieht, wird für das schwache Ereignismuster die folgende Syntax verwendet:
ClockwiseSpinEventManager.AddListener(spinnerInstance, this);
Die Behandlungslogik für dieses Ereignis ist dann in einer der Implementierungen von ReceiveWeakEvent in der Klasse festgelegt, und nicht als herkömmlicher delegatbasierter Handler.
Implementieren des Musters für externe Ereignisse
Ein interessanter Aspekt des schwachen Ereignismusters ist die Tatsache, dass Sie das Muster auch auf ein Ereignis anwenden können, das nicht Teil Ihrer Codebasis ist. Aus Sicht der Quelle unterscheidet sich die Art und Weise, in der Handler an das Ereignis angefügt werden, nicht und wird vom WeakEventManager gesteuert. Sie müssen nur einen WeakEventManager für dieses Ereignis definieren und dieses Ereignis dann als Teil der ReceiveWeakEvent-Logik für jeden potenziellen Listener, der das schwache Ereignismuster zum Überwachen dieses Ereignisses verwenden möchte, berücksichtigen.