Share via


WeakEvent-Muster

Aktualisiert: November 2007

In normalen 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 WeakEvent-Muster bezeichnet.

Gründe zum Implementieren des WeakEvent-Musters

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 für die Quelle beeinflusst wird (es sei denn, der Ereignishandler wird explizit entfernt). In bestimmten Situationen soll die Objektlebensdauer des Listeners möglicherweise jedoch nur 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 WeakEvent-Muster ist darauf ausgerichtet, dieses Speicherverlustproblem zu lösen. Das WeakEvent-Muster kann dann verwendet werden, wenn sich ein Listener für ein Ereignis registrieren muss, jedoch nicht genau bekannt ist, wann die Registrierung wieder aufgehoben werden soll. Ebenso kann es verwendet werden, wenn die Objektlebensdauer der Quelle die "nützliche" Objektlebensdauer des Listeners überschreitet. (Was "nützlich" ist, hängt von Ihrer eigenen Definition ab.) Das WeakEvent-Muster 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 von der Quelle hat tatsächlich keine Auswirkungen auf die Entscheidung, ob der Listener für die Garbage Collection freigegeben wird. Der Verweis ist ein schwacher Verweis, woher die Bezeichnung WeakEvent-Muster 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 WeakEvent-Muster implementieren?

Das Implementieren des WeakEvent-Musters ist in erster Linie für Autoren von Steuerelementen interessant. Dies ist darauf zurückzuführen, dass der Steuerelementautor im Großen und Ganzen für das Verhalten und die Kapselung des Steuerelements sowie für die Auswirkungen, die es auf die Anwendung hat, in die es eingefügt wird, verantwortlich ist. Dies schließt das Objektlebensdauer-Verhalten des Steuerelements ein, insbesondere die Behandlung des beschriebenen Speicherverlustproblems.

Bestimmte Szenarien sind grundsätzlich zur Anwendung des WeakEvent-Musters sehr gut geeignet. Eines dieser Szenarien ist die Datenbindung. Hierbei kommt es häufig vor, dass ein Quellobjekt, bei dem es sich um eine Datenquelle handelt, vollkommen unabhängig von einem Listenerobjekt ist, das das Ziel einer Bindung darstellt. In zahlreichen Aspekten der WPF-Datenbindung wird das WeakEvent-Muster bereits durch die Implementierung der Ereignisse angewendet.

Implementieren des WeakEvent-Musters

Das Implementieren des WeakEvent-Musters umfasst 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 für dieses Ereignis.

WeakEventManager

Normalerweise erstellen Sie Managerklassen bei einer 1:1-Beziehung zu Ereignissen, die das Muster implementieren. Wenn Sie zum Beispiel über ein Spin-Ereignis verfügen, leiten Sie eine SpinEventManager-Klasse als dedizierten WeakEvent-Manager für das Ereignis ab. 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.

Die Implementierungscheckliste zum Ableiten von der WeakEventManager-Klasse besteht im Überschreiben von zwei virtuellen Methoden und dem Bereitstellen von mehreren anderen Membern, deren Namen nicht ausdrücklich von einer virtuellen Vorlage bestimmt sein müssen, die jedoch trotzdem vorhanden sein müssen. Die Überschreibungen dienen dazu, den Ereigniszustellungsmodus durch die WPF-Infrastruktur zu initialisieren oder zu beenden. Die anderen Member sind notwendig, um Funktionen bereitzustellen, damit Ihre eigenen IWeakEventListener-Implementierungen den WeakEventManager zum Anfügen von Listenern an das Ereignis verwenden können.

Ausführliche Implementierungshinweise zum Ableiten von WeakEventManager, finden Sie im Abschnitt mit den Hinweisen zur Vererbung im WeakEventManager-Referenzthema.

IWeakEventListener

Eine Klasse, die IWeakEventListener implementiert, hat nur eine Aufgabe: das Implementieren der ReceiveWeakEvent-Schnittstellenmethode. Die ReceiveWeakEvent-Implementierung muss eine zentrale Implementierung sein, die jeden Ereignisverweis, der in dieser Klasse vorhanden ist, an den entsprechenden WeakEventManager weiterleitet.

Ausführliche Hinweise zur Implementierung der IWeakEventListener-Schnittstelle finden Sie im Abschnitt mit den Hinweisen zur Implementierung im ReceiveWeakEvent-Methodenreferenzthema.

Anfügen von Listenern

Angenommen, Sie verfügen über ein ClockwiseSpin-Ereignis (definiert durch Spinner), bei dem es sich um ein herkömmliches Ereignis handelt. Um das Muster für dieses Ereignis zu verwenden, verwenden Sie entweder eine vorhandene ClockwiseSpinEventManager-Klasse, die von WeakEventManager abgeleitet ist, oder Sie implementieren sie selbst. 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 der +=-Syntax:

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 WeakEvent-Muster stattdessen 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 WeakEvent-Musters 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 Muster zum Überwachen dieses Ereignisses verwenden möchte, berücksichtigen.

Siehe auch

Konzepte

Übersicht über Routingereignisse

Übersicht über Datenbindung

Referenz

WeakEventManager

IWeakEventListener