Modèles d'événement faibleWeak Event Patterns

Dans les applications, il est possible que les gestionnaires qui sont attachés à des sources d’événements ne soient pas détruits avec l’objet d’écouteur qui a attaché le gestionnaire à la source.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. Cette situation peut entraîner des fuites de mémoire.This situation can lead to memory leaks. Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) introduit un modèle de conception qui peut être utilisé pour résoudre ce problème, en fournissant une classe de gestionnaire dédiée pour des événements particuliers et en implémentant une interface sur les écouteurs de cet événement.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. Ce modèle de conception est connu sous le nom de modèle d’événement faible.This design pattern is known as the weak event pattern.

Pourquoi implémenter le modèle d’événement faible ?Why Implement the Weak Event Pattern?

L’écoute des événements peut entraîner des fuites de mémoire.Listening for events can lead to memory leaks. La technique classique pour écouter un événement consiste à utiliser la syntaxe spécifique au langage qui attache un gestionnaire à un événement sur une source.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. Par exemple, dans C#, cette syntaxe est la suivante : source.SomeEvent += new SomeEventHandler(MyEventHandler).For example, in C#, that syntax is: source.SomeEvent += new SomeEventHandler(MyEventHandler).

Cette technique crée une référence forte de la source de l’événement à l’écouteur d’événements.This technique creates a strong reference from the event source to the event listener. En règle générale, l’attachement d’un gestionnaire d’événements pour un écouteur amène l’écouteur à avoir une durée de vie d’objet influencée par la durée de vie de l’objet de la source (sauf si le gestionnaire d’événements est supprimé explicitement).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). Toutefois, dans certaines circonstances, vous souhaiterez peut-être que la durée de vie des objets de l’écouteur soit contrôlée par d’autres facteurs, par exemple s’il appartient actuellement à l’arborescence d’éléments visuels de l’application, et non par la durée de vie de la source.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. Chaque fois que la durée de vie de l’objet source s’étend au-delà de la durée de vie des objets de l’écouteur, le modèle d’événement normal entraîne une fuite de mémoire : l’écouteur reste actif plus longtemps que prévu.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.

Le modèle d’événement faible est conçu pour résoudre ce problème de fuite de mémoire.The weak event pattern is designed to solve this memory leak problem. Le modèle d’événement faible peut être utilisé chaque fois qu’un écouteur doit s’inscrire pour un événement, mais l’écouteur ne sait pas explicitement quand annuler l’inscription.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. Le modèle d’événement faible peut également être utilisé chaque fois que la durée de vie d’objet de la source dépasse la durée de vie d’objet utile de l’écouteur.The weak event pattern can also be used whenever the object lifetime of the source exceeds the useful object lifetime of the listener. (Dans ce cas, utile est déterminé par vous.) Le modèle d’événement faible permet à l’écouteur de s’inscrire pour recevoir l’événement sans affecter les caractéristiques de durée de vie des objets de l’écouteur de quelque manière que ce soit.(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. En effet, la référence implicite de la source ne détermine pas si l’écouteur est éligible pour garbage collection.In effect, the implied reference from the source does not determine whether the listener is eligible for garbage collection. La référence est une référence faible, donc la dénomination du modèle d’événement faible et les API associées.The reference is a weak reference, thus the naming of the weak event pattern and the related APIs. L’écouteur peut être récupéré par le garbage collector ou détruit, et la source peut continuer sans conserver les références de gestionnaire non collectées à un objet désormais détruit.The listener can be garbage collected or otherwise destroyed, and the source can continue without retaining noncollectible handler references to a now destroyed object.

Qui doit implémenter le modèle d’événement faible ?Who Should Implement the Weak Event Pattern?

L’implémentation du modèle d’événement faible est particulièrement intéressante pour les auteurs de contrôle.Implementing the weak event pattern is interesting primarily for control authors. En tant qu’auteur de contrôle, vous êtes en grande partie responsable du comportement et de la relation contenant-contenu de votre contrôle, ainsi que de l’impact qu’il a sur les applications dans lesquelles il est inséré.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. Cela comprend le comportement de durée de vie des objets de contrôle, en particulier la gestion du problème de fuite de mémoire décrit.This includes the control object lifetime behavior, in particular the handling of the described memory leak problem.

Certains scénarios se prêtent par nature à l’application du modèle d’événement faible.Certain scenarios inherently lend themselves to the application of the weak event pattern. La liaison de données est un scénario de ce type.One such scenario is data binding. Dans la liaison de données, il est courant que l’objet source soit complètement indépendant de l’objet d’écouteur, qui est la cible d’une liaison.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. De nombreux aspects de WPFWPF la liaison de données ont déjà le modèle d’événement faible qui est appliqué dans la manière dont les événements sont implémentés.Many aspects of WPFWPF data binding already have the weak event pattern applied in how the events are implemented.

Comment implémenter le modèle d’événement faibleHow to Implement the Weak Event Pattern

Il existe trois façons d’implémenter un modèle d’événement faible.There are three ways to implement weak event pattern. Le tableau suivant répertorie les trois approches et fournit des conseils sur l’utilisation de chacune d’elles.The following table lists the three approaches and provides some guidance for when you should use each.

ApprocheApproach Quand implémenterWhen to Implement
Utiliser une classe de gestionnaire d’événements faible existanteUse an existing weak event manager class Si l’événement auquel vous souhaitez vous abonner a une WeakEventManagercorrespondante, utilisez le gestionnaire d’événements faible existant.If the event you want to subscribe to has a corresponding WeakEventManager, use the existing weak event manager. Pour obtenir la liste des gestionnaires d’événements faibles inclus dans WPF, consultez la hiérarchie d’héritage dans la classe WeakEventManager.For a list of weak event managers that are included with WPF, see the inheritance hierarchy in the WeakEventManager class. Étant donné que les gestionnaires d’événements faibles inclus sont limités, vous devrez probablement choisir l’une des autres approches.Because the included weak event managers are limited, you will probably need to choose one of the other approaches.
Utiliser une classe de gestionnaire d’événements faible génériqueUse a generic weak event manager class Utilisez une WeakEventManager<TEventSource,TEventArgs> générique lorsqu’un WeakEventManager existant n’est pas disponible, vous avez besoin d’un moyen simple d’implémenter et vous ne vous inquiétez pas de l’efficacité.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. Le WeakEventManager<TEventSource,TEventArgs> générique est moins efficace qu’un gestionnaire d’événements faible existant ou personnalisé.The generic WeakEventManager<TEventSource,TEventArgs> is less efficient than an existing or custom weak event manager. Par exemple, la classe générique fait plus de réflexion pour découvrir l’événement en fonction du nom de l’événement.For example, the generic class does more reflection to discover the event given the event's name. En outre, le code permettant d’enregistrer l’événement à l’aide de la WeakEventManager<TEventSource,TEventArgs> générique est plus détaillé que l’utilisation d’une WeakEventManagerexistante ou personnalisée.Also, the code to register the event by using the generic WeakEventManager<TEventSource,TEventArgs> is more verbose than using an existing or custom WeakEventManager.
Créer une classe de gestionnaire d’événements faibles personnaliséeCreate a custom weak event manager class Créez un WeakEventManager personnalisé lorsqu’un WeakEventManager existant n’est pas disponible et que vous souhaitez obtenir une efficacité optimale.Create a custom WeakEventManager when an existing WeakEventManager is not available and you want the best efficiency. L’utilisation d’un WeakEventManager personnalisé pour s’abonner à un événement est plus efficace, mais vous encourez le coût de l’écriture de code supplémentaire au début.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.
Utiliser un gestionnaire d’événements faibles tiersUse a third-party weak event manager NuGet possède plusieurs gestionnaires d’événements faibles et de nombreuses infrastructures WPF prennent également en charge le modèle (par exemple, consultez la documentation de Prism sur l’abonnement aux événements faiblement couplés).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).

Les sections suivantes décrivent comment implémenter le modèle d’événement faible.The following sections describe how to implement the weak event pattern. Dans le cadre de cette discussion, l’événement auquel s’abonner a les caractéristiques suivantes.For purposes of this discussion, the event to subscribe to has the following characteristics.

  • Le nom de l’événement est SomeEvent.The event name is SomeEvent.

  • L’événement est déclenché par la classe EventSource.The event is raised by the EventSource class.

  • Le gestionnaire d’événements a le type : SomeEventEventHandler (ou EventHandler<SomeEventEventArgs>).The event handler has type: SomeEventEventHandler (or EventHandler<SomeEventEventArgs>).

  • L’événement passe un paramètre de type SomeEventEventArgs aux gestionnaires d’événements.The event passes a parameter of type SomeEventEventArgs to the event handlers.

Utilisation d’une classe de gestionnaire d’événements faible existanteUsing an Existing Weak Event Manager Class

  1. Recherchez un gestionnaire d’événements faible existant.Find an existing weak event manager.

    Pour obtenir la liste des gestionnaires d’événements faibles inclus dans WPF, consultez la hiérarchie d’héritage dans la classe WeakEventManager.For a list of weak event managers that are included with WPF, see the inheritance hierarchy in the WeakEventManager class.

  2. Utilisez le nouveau gestionnaire d’événements faible plutôt que le raccordement d’événement normal.Use the new weak event manager instead of the normal event hookup.

    Par exemple, si votre code utilise le modèle suivant pour s’abonner à un événement :For example, if your code uses the following pattern to subscribe to an event:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);  
    

    Remplacez-le par le modèle suivant :Change it to the following pattern:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);  
    

    De même, si votre code utilise le modèle suivant pour se désabonner d’un événement :Similarly, if your code uses the following pattern to unsubscribe from an event:

    source.SomeEvent -= new SomeEventEventHandler(OnSomeEvent);  
    

    Remplacez-le par le modèle suivant :Change it to the following pattern:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);  
    

Utilisation de la classe de gestionnaire d’événements faible génériqueUsing the Generic Weak Event Manager Class

  1. Utilisez la classe de WeakEventManager<TEventSource,TEventArgs> générique au lieu du raccordement d’événement normal.Use the generic WeakEventManager<TEventSource,TEventArgs> class instead of the normal event hookup.

    Quand vous utilisez WeakEventManager<TEventSource,TEventArgs> pour inscrire des écouteurs d’événements, vous fournissez la source de l’événement et EventArgs type en tant que paramètres de type à la classe et appelez AddHandler comme indiqué dans le code suivant :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);  
    

Création d’une classe de gestionnaire d’événements faibles personnaliséeCreating a Custom Weak Event Manager Class

  1. Copiez le modèle de classe suivant dans votre projet.Copy the following class template to your project.

    Cette classe hérite de la classe WeakEventManager.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. Remplacez le nom du SomeEventWeakEventManager par votre propre nom.Replace the SomeEventWeakEventManager name with your own name.

  3. Remplacez les trois noms décrits précédemment par les noms correspondants pour votre événement.Replace the three names described previously with the corresponding names for your event. (SomeEvent, EventSourceet SomeEventEventArgs)(SomeEvent, EventSource, and SomeEventEventArgs)

  4. Affectez à la visibilité (publique/interne/privée) de la classe de gestionnaire d’événements faible la même visibilité que l’événement qu’elle gère.Set the visibility (public / internal / private) of the weak event manager class to the same visibility as event it manages.

  5. Utilisez le nouveau gestionnaire d’événements faible plutôt que le raccordement d’événement normal.Use the new weak event manager instead of the normal event hookup.

    Par exemple, si votre code utilise le modèle suivant pour s’abonner à un événement :For example, if your code uses the following pattern to subscribe to an event:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);  
    

    Remplacez-le par le modèle suivant :Change it to the following pattern:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);  
    

    De même, si votre code utilise le modèle suivant pour annuler l’abonnement à un événement :Similarly, if your code uses the following pattern to unsubscribe to an event:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);  
    

    Remplacez-le par le modèle suivant :Change it to the following pattern:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);  
    

Voir aussiSee also