Schwache EreignismusterWeak Event Patterns

In-Anwendungen ist es möglich, dass an Ereignis Quellen angefügte Handler nicht in Abstimmung mit dem Listenerobjekt zerstört werden, das den Handler an die Quelle angefügt hat.In applications, it is possible that handlers that are attached to event sources will not be destroyed in coordination with the listener object that attached the handler to the source. Diese Situation kann zu Speicher Verlusten führen.This situation can lead to memory leaks. Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF)führt ein Entwurfsmuster ein, das verwendet werden kann, um dieses Problem zu beheben, indem eine dedizierte Manager-Klasse für bestimmte Ereignisse bereitgestellt und eine Schnittstelle für Listener für dieses Ereignis implementiert wird.introduces a design pattern that can be used to address this issue, by providing a dedicated manager class for particular events and implementing an interface on listeners for that event. Dieses Entwurfsmuster wird als schwaches Ereignis Musterbezeichnet.This design pattern is known as the weak event pattern.

Gründe für die Implementierung des schwachen Ereignis MustersWhy Implement the Weak Event Pattern?

Das Lauschen auf Ereignisse kann zu Speicher Verlusten führen.Listening for events can lead to memory leaks. Die typische Vorgehensweise zum lauschen auf ein Ereignis besteht darin, die sprachspezifische Syntax zu verwenden, die einen Handler an ein Ereignis in einer Quelle anfügt.The typical technique for listening to an event is to use the language-specific syntax that attaches a handler to an event on a source. In C#lautet die Syntax z. b.: source.SomeEvent += new SomeEventHandler(MyEventHandler).For example, in C#, that syntax is: source.SomeEvent += new SomeEventHandler(MyEventHandler).

Diese Technik erstellt einen starken Verweis von der Ereignis Quelle auf den Ereignislistener.This technique creates a strong reference from the event source to the event listener. Normalerweise bewirkt das Anfügen eines Ereignis Handlers für einen Listener, dass der Listener eine Objekt Lebensdauer hat, die von der Objekt Lebensdauer der Quelle beeinflusst wird (es sei denn, der Ereignishandler wird explizit entfernt).Ordinarily, attaching an event handler for a listener causes the listener to have an object lifetime that is influenced by the object lifetime of the source (unless the event handler is explicitly removed). Unter bestimmten Umständen möchten Sie jedoch möglicherweise, dass die Objekt Lebensdauer des Listener von anderen Faktoren gesteuert wird, z. b. ob er derzeit zur visuellen Struktur der Anwendung gehört, und nicht nach der Lebensdauer der Quelle.But in certain circumstances, you might want the object lifetime of the listener to be controlled by other factors, such as whether it currently belongs to the visual tree of the application, and not by the lifetime of the source. Wenn die Lebensdauer des Quell Objekts über die Objekt Lebensdauer des Listener hinausgeht, führt das normale Ereignis Muster zu einem Speicherleck: der Listener bleibt länger als beabsichtigt.Whenever the source object lifetime extends beyond the object lifetime of the listener, the normal event pattern leads to a memory leak: the listener is kept alive longer than intended.

Das schwache Ereignis Muster ist so konzipiert, dass dieses Speicherlecks-Problem gelöst wird.The weak event pattern is designed to solve this memory leak problem. Das schwache Ereignis Muster kann immer dann verwendet werden, wenn ein Listener für ein Ereignis registriert werden muss, aber der Listener weiß nicht explizit, wann die Registrierung aufgehoben werden soll.The weak event pattern can be used whenever a listener needs to register for an event, but the listener does not explicitly know when to unregister. Das schwache Ereignis Muster kann auch verwendet werden, wenn die Objekt Lebensdauer der Quelle die nützliche Objekt Lebensdauer des Listener überschreitet.The weak event pattern can also be used whenever the object lifetime of the source exceeds the useful object lifetime of the listener. (In diesem Fall wird nützlich von Ihnen bestimmt.) Das schwache Ereignis Muster ermöglicht dem Listener, sich für das Ereignis zu registrieren und das Ereignis zu empfangen, ohne die Eigenschaften der Objekt Lebensdauer in irgendeiner Weise zu beeinträchtigen.(In this case, useful is determined by you.) The weak event pattern allows the listener to register for and receive the event without affecting the object lifetime characteristics of the listener in any way. Tatsächlich bestimmt der implizierte Verweis aus der Quelle nicht, ob der Listener für Garbage Collection geeignet ist.In effect, the implied reference from the source does not determine whether the listener is eligible for garbage collection. Der Verweis ist eine schwache Referenz und somit die Benennung des schwachen Ereignis Musters und der zugehörigen APIs.The reference is a weak reference, thus the naming of the weak event pattern and the related APIs. Der Listener kann auf eine Garbage Collection oder anderweitig zerstört werden, und die Quelle kann fortgesetzt werden, ohne dass nicht entladbare Handlerverweise auf ein jetzt zerstörtes Objekt beibehalten werden.The listener can be garbage collected or otherwise destroyed, and the source can continue without retaining noncollectible handler references to a now destroyed object.

Wer sollte das schwache Ereignis Muster implementieren?Who Should Implement the Weak Event Pattern?

Die Implementierung des schwachen Ereignis Musters ist hauptsächlich für Autoren von Steuerelementen interessant.Implementing the weak event pattern is interesting primarily for control authors. Als Autor eines Steuer Elements sind Sie größtenteils verantwortlich für das Verhalten und die Kapselung Ihres Steuer Elements und die Auswirkungen, die es auf Anwendungen hat, in die es eingefügt wurde.As a control author, you are largely responsible for the behavior and containment of your control and the impact it has on applications in which it is inserted. Dies schließt das Verhalten der Steuerungsobjekt Lebensdauer ein, insbesondere die Behandlung des beschriebenen Speicherlecks Problems.This includes the control object lifetime behavior, in particular the handling of the described memory leak problem.

Bestimmte Szenarien sind von Natur aus für die Anwendung des schwachen Ereignis Musters geeignet.Certain scenarios inherently lend themselves to the application of the weak event pattern. Ein solches Szenario ist die Datenbindung.One such scenario is data binding. Bei der Datenbindung ist es üblich, dass das Quell Objekt vollständig unabhängig vom Listenerobjekt ist, das das Ziel einer Bindung ist.In data binding, it is common for the source object to be completely independent of the listener object, which is a target of a binding. Viele Aspekte der WPFWPF Datenbindung verfügen bereits über das schwache Ereignis Muster, das in der Implementierung der Ereignisse angewendet wird.Many aspects of WPFWPF data binding already have the weak event pattern applied in how the events are implemented.

So implementieren Sie das schwache Ereignis MusterHow to Implement the Weak Event Pattern

Es gibt drei Möglichkeiten, ein schwaches Ereignis Muster zu implementieren.There are three ways to implement weak event pattern. In der folgenden Tabelle werden die drei Ansätze aufgelistet und eine Anleitung für die Verwendung der einzelnen Methoden bereitstellt.The following table lists the three approaches and provides some guidance for when you should use each.

AnsatzApproach Zeitpunkt der ImplementierungWhen to Implement
Vorhandene schwache Ereignis-Manager-Klasse verwendenUse an existing weak event manager class Wenn das Ereignis, das Sie abonnieren möchten, über ein WeakEventManagerentsprechendes Ereignis verfügt, verwenden Sie den vorhandenen schwachen Ereignis-Manager.If the event you want to subscribe to has a corresponding WeakEventManager, use the existing weak event manager. Eine Liste der in WPF enthaltenen schwachen Ereignis-Manager finden Sie in der Vererbungs Hierarchie in WeakEventManager der-Klasse.For a list of weak event managers that are included with WPF, see the inheritance hierarchy in the WeakEventManager class. Da die enthaltenen schwachen Ereignis-Manager eingeschränkt sind, müssen Sie wahrscheinlich einen der anderen Ansätze auswählen.Because the included weak event managers are limited, you will probably need to choose one of the other approaches.
Verwenden einer generischen Weak EventManager-KlasseUse a generic weak event manager class Verwenden Sie einen WeakEventManager<TEventSource,TEventArgs> generischen, WeakEventManager wenn eine vorhandene nicht verfügbar ist, Sie möchten eine einfache Implementierung durchführen und sich keine Gedanken um die Effizienz machen.Use a generic WeakEventManager<TEventSource,TEventArgs> when an existing WeakEventManager is not available, you want an easy way to implement, and you are not concerned about efficiency. Der generische WeakEventManager<TEventSource,TEventArgs> ist weniger effizient als ein vorhandener oder benutzerdefinierter schwacher Ereignis-Manager.The generic WeakEventManager<TEventSource,TEventArgs> is less efficient than an existing or custom weak event manager. Die generische Klasse führt z. b. mehr Reflektion aus, um das Ereignis anhand des Ereignis namens zu ermitteln.For example, the generic class does more reflection to discover the event given the event's name. Außerdem ist der Code zum Registrieren des Ereignisses mithilfe der generischen WeakEventManager<TEventSource,TEventArgs> -Methode ausführlicher als die Verwendung eines vorhandenen oder Benutzer WeakEventManagerdefinierten.Also, the code to register the event by using the generic WeakEventManager<TEventSource,TEventArgs> is more verbose than using an existing or custom WeakEventManager.
Erstellen einer benutzerdefinierten Weak EventManager-KlasseCreate a custom weak event manager class Erstellen Sie eine WeakEventManager benutzerdefinierte, WeakEventManager wenn eine vorhandene nicht verfügbar ist und Sie die beste Effizienz erzielen möchten.Create a custom WeakEventManager when an existing WeakEventManager is not available and you want the best efficiency. Die Verwendung eines WeakEventManager benutzerdefinierten zum Abonnieren eines Ereignisses ist effizienter, aber Sie verursachen die Kosten für das Schreiben von mehr Code am Anfang.Using a custom WeakEventManager to subscribe to an event will be more efficient, but you do incur the cost of writing more code at the beginning.
Verwenden eines schwachen Ereignis-Managers von DrittanbieternUse a third-party weak event manager Nuget verfügt über mehrere schwache Ereignis-Manager , und viele WPF-Frameworks unterstützen auch das-Muster (z.b. in der Prism-Dokumentation zu locker verknüpften Ereignis Abonnements).NuGet has several weak event managers and many WPF frameworks also support the pattern (for instance, see Prism's documentation on loosely coupled event subscription).

In den folgenden Abschnitten wird beschrieben, wie das schwache Ereignis Muster implementiert wird.The following sections describe how to implement the weak event pattern. Im Rahmen dieser Erläuterung weist das Ereignis, das abonniert werden soll, die folgenden Eigenschaften auf.For purposes of this discussion, the event to subscribe to has the following characteristics.

  • Der Ereignis Name ist SomeEvent.The event name is SomeEvent.

  • Das-Ereignis wird von der EventSource -Klasse ausgelöst.The event is raised by the EventSource class.

  • Der Ereignishandler weist den Typ SomeEventEventHandler auf: EventHandler<SomeEventEventArgs>(oder).The event handler has type: SomeEventEventHandler (or EventHandler<SomeEventEventArgs>).

  • Das-Ereignis übergibt einen Parameter vom SomeEventEventArgs Typ an die Ereignishandler.The event passes a parameter of type SomeEventEventArgs to the event handlers.

Verwenden einer vorhandenen Weak Event Manager-KlasseUsing an Existing Weak Event Manager Class

  1. Suchen Sie einen vorhandenen schwachen Ereignis-Manager.Find an existing weak event manager.

    Eine Liste der in WPF enthaltenen schwachen Ereignis-Manager finden Sie in der Vererbungs Hierarchie in WeakEventManager der-Klasse.For a list of weak event managers that are included with WPF, see the inheritance hierarchy in the WeakEventManager class.

  2. Verwenden Sie den neuen schwachen Ereignis-Manager anstelle der normalen Ereignis Einbindung.Use the new weak event manager instead of the normal event hookup.

    Wenn Ihr Code z. b. das folgende Muster verwendet, um ein Ereignis zu abonnieren:For example, if your code uses the following pattern to subscribe to an event:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);  
    

    Ändern Sie den Wert in das folgende Muster:Change it to the following pattern:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);  
    

    Ebenso, wenn Ihr Code das folgende Muster verwendet, um ein Ereignis abzubestellen:Similarly, if your code uses the following pattern to unsubscribe from an event:

    source.SomeEvent -= new SomeEventEventHandler(OnSomeEvent);  
    

    Ändern Sie den Wert in das folgende Muster:Change it to the following pattern:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);  
    

Verwenden der generischen Weak Event Manager-KlasseUsing the Generic Weak Event Manager Class

  1. Verwenden Sie die WeakEventManager<TEventSource,TEventArgs> generische-Klasse anstelle der normalen Ereignis Einbindung.Use the generic WeakEventManager<TEventSource,TEventArgs> class instead of the normal event hookup.

    Wenn Sie zum WeakEventManager<TEventSource,TEventArgs> Registrieren von Ereignislistenern verwenden, geben Sie EventArgs die Ereignis Quelle und den Typ als Typparameter AddHandler für die Klasse an, und geben Sie an, wie im folgenden Code gezeigt:When you use WeakEventManager<TEventSource,TEventArgs> to register event listeners, you supply the event source and EventArgs type as the type parameters to the class and call AddHandler as shown in the following code:

    WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);  
    

Erstellen einer benutzerdefinierten Weak Event Manager-KlasseCreating a Custom Weak Event Manager Class

  1. Kopieren Sie die folgende Klassen Vorlage in Ihr Projekt.Copy the following class template to your project.

    Diese Klasse erbt von der WeakEventManager -Klasse.This class inherits from the WeakEventManager class.

    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);
        }
    }
    
  2. Ersetzen Sie SomeEventWeakEventManager den Namen durch ihren eigenen Namen.Replace the SomeEventWeakEventManager name with your own name.

  3. Ersetzen Sie die drei zuvor beschriebenen Namen durch die entsprechenden Namen für das Ereignis.Replace the three names described previously with the corresponding names for your event. (SomeEvent, EventSourceund )SomeEventEventArgs(SomeEvent, EventSource, and SomeEventEventArgs)

  4. Legen Sie die Sichtbarkeit (Public/Internal/private) der Weak EventManager-Klasse auf die gleiche Sichtbarkeit wie das Ereignis fest, das Sie verwaltet.Set the visibility (public / internal / private) of the weak event manager class to the same visibility as event it manages.

  5. Verwenden Sie den neuen schwachen Ereignis-Manager anstelle der normalen Ereignis Einbindung.Use the new weak event manager instead of the normal event hookup.

    Wenn Ihr Code z. b. das folgende Muster verwendet, um ein Ereignis zu abonnieren:For example, if your code uses the following pattern to subscribe to an event:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);  
    

    Ändern Sie den Wert in das folgende Muster:Change it to the following pattern:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);  
    

    Ebenso, wenn Ihr Code das folgende Muster verwendet, um ein Ereignis abzubestellen:Similarly, if your code uses the following pattern to unsubscribe to an event:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);  
    

    Ändern Sie den Wert in das folgende Muster:Change it to the following pattern:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);  
    

Siehe auchSee also