Modèles d’événements faibles
Dans les applications, il est possible que les gestionnaires attachés à des sources d’événements ne soient pas détruits en coordination avec l’objet écouteur qui a attaché le gestionnaire à la source. Cette situation peut entraîner des fuites de mémoire. 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 pour cet événement. Ce modèle de conception est appelé modèle d’événement faible .
Pourquoi implémenter le modèle d’événement faible ?
L’écoute des événements peut entraîner des fuites de mémoire. La technique classique d’écoute d’un événement consiste à utiliser la syntaxe spécifique au langage qui attache un gestionnaire à un événement sur une source. Par exemple, en C#, cette syntaxe est : source.SomeEvent += new SomeEventHandler(MyEventHandler)
.
Cette technique établit une référence forte entre la source de l'événement et l'écouteur d'événement. En règle générale, attacher un gestionnaire d'événements à un écouteur entraîne l’écouteur à avoir une durée de vie d’objet qui est influencée par la durée de vie de l’objet source (sauf si le gestionnaire d’événements est explicitement supprimé). Toutefois, dans certaines circonstances, vous souhaiterez peut-être que la durée de vie de l’objet de l’écouteur soit contrôlée par d’autres facteurs, comme s’il appartient actuellement à l’arborescence visuelle de l’application, et non par la durée de vie de la source. Chaque fois que la durée de vie de l’objet source dépasse la durée de vie de l’objet de l’écouteur, le modèle d’événement normal entraîne une fuite de mémoire : l’écouteur est conservé plus longtemps que prévu.
Le modèle d’événement faible est conçu pour résoudre ce problème de fuite de mémoire. Le modèle d’événement faible peut être utilisé chaque fois qu’un écouteur doit s’inscrire à un événement, mais l’écouteur ne sait pas explicitement quand annuler l’inscription. Le modèle d’événement faible peut également être utilisé chaque fois que la durée de vie de l’objet de la source dépasse la durée de vie de l’objet utile de l’écouteur. (Dans ce cas, utile est déterminé par vous.) Le modèle d’événement faible permet à l’écouteur de s'enregistrer et de recevoir l’événement sans affecter les caractéristiques de durée de vie de l’écouteur de quelque manière que ce soit. En effet, la référence implicite de la source ne détermine pas si l'écouteur est éligible au ramasse-miettes. La référence est une référence faible, d'où le nommage du modèle d'événement faible et des API associées. L'écouteur peut être collecté par le ramasse-miettes ou autrement détruit, et la source peut continuer sans conserver les références de gestionnaire impossibles à collecter à un objet maintenant détruit.
Qui doit implémenter le modèle d’événement faible ?
L’implémentation du modèle d’événement faible est intéressante principalement pour les auteurs de contrôles. En tant qu’auteur de contrôle, vous êtes en grande partie responsable du comportement et de la contenance de votre contrôle et de l’impact qu’il a sur les applications dans lesquelles il est inséré. Cela inclut 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.
Certains scénarios se prêtent intrinsèquement à l’application du modèle d’événement faible. L’un de ces scénarios est la liaison de données. Dans la liaison de données, il est courant que l’objet source soit complètement indépendant de l’objet écouteur, qui est une cible d’une liaison. De nombreux aspects de la liaison de données WPF ont déjà adopté le modèle d’événement faible dans la façon dont les événements sont implémentés.
Comment implémenter le modèle d’événement faible
Il existe trois façons d’implémenter un modèle d’événement faible. Le tableau suivant répertorie les trois approches et fournit des conseils pour chaque utilisation.
Approche | Quand implémenter |
---|---|
Utiliser une classe de gestionnaire d’événements faible existante | Si l’événement auquel vous souhaitez vous abonner a un WeakEventManagercorrespondant, utilisez le gestionnaire d’événements faible existant. Pour obtenir la liste des gestionnaires d’événements faibles inclus dans WPF, consultez la hiérarchie d’héritage dans la classe WeakEventManager. Étant donné que les gestionnaires d’événements faibles inclus sont limités, vous devrez probablement choisir l’une des autres approches. |
Utiliser une classe de gestionnaire d’événements faible générique | Utilisez un WeakEventManager<TEventSource,TEventArgs> générique lorsqu’une WeakEventManager existante n’est pas disponible, vous souhaitez un moyen facile d’implémenter et vous n’êtes pas préoccupé par l’efficacité. Le WeakEventManager<TEventSource,TEventArgs> générique est moins efficace qu’un gestionnaire d’événements faible existant ou personnalisé. Par exemple, la classe générique effectue davantage de réflexion pour découvrir l’événement en fonction du nom de l’événement. En outre, le code permettant d’inscrire l’événement à l’aide du WeakEventManager<TEventSource,TEventArgs> générique est plus détaillé que d’utiliser un WeakEventManagerexistant ou personnalisé. |
Créer une classe de gestionnaire d’événements faible personnalisée | Créez un WeakEventManager personnalisé lorsqu’un WeakEventManager existant n’est pas disponible et que vous souhaitez optimiser l’efficacité. L’utilisation d’un WeakEventManager personnalisé pour s’abonner à un événement sera plus efficace, mais vous allez devoir écrire plus de code au début, ce qui représente un coût. |
Utiliser un gestionnaire d’événements faible tiers | NuGet a plusieurs gestionnaires d’événements faibles et de nombreux frameworks WPF prennent également en charge le modèle. |
Les sections suivantes décrivent comment implémenter le modèle d’événement faible. À des fins de cette discussion, l’événement auquel s’abonner a les caractéristiques suivantes.
Le nom de l’événement est
SomeEvent
.L’événement est déclenché par la classe
EventSource
.Le gestionnaire d’événements a le type :
SomeEventEventHandler
(ouEventHandler<SomeEventEventArgs>
).L’événement transmet un paramètre de type
SomeEventEventArgs
aux gestionnaires d’événements.
Utilisation d’une classe Gestionnaire d’événements faible existante
Recherchez un gestionnaire d’événements faible existant.
Pour obtenir la liste des gestionnaires d’événements faibles inclus dans WPF, consultez la hiérarchie d’héritage dans la classe WeakEventManager.
Utilisez le nouveau gestionnaire d’événements faible au lieu du raccordement d’événements normal.
Par exemple, si votre code utilise le modèle suivant pour s’abonner à un événement :
source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
Remplacez-le par le modèle suivant :
SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
De même, si votre code utilise le modèle suivant pour se désabonner d’un événement :
source.SomeEvent -= new SomeEventEventHandler(OnSomeEvent);
Remplacez-le par le modèle suivant :
SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
Utilisation de la classe Gestionnaire d’événements faibles générique
Utilisez plutôt la classe générique WeakEventManager<TEventSource,TEventArgs> au lieu de la liaison normale des événements.
Lorsque vous utilisez WeakEventManager<TEventSource,TEventArgs> pour inscrire des écouteurs d’événements, vous fournissez la source d’événement et le type EventArgs comme paramètres de type à la classe, puis appelez AddHandler comme le montre le code suivant :
WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);
Création d’une classe Gestionnaire d’événements faible personnalisée
Copiez le modèle de classe suivant dans votre projet.
Cette classe hérite de la classe WeakEventManager.
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); } }
Remplacez le nom
SomeEventWeakEventManager
par votre propre nom.Remplacez les trois noms décrits précédemment par les noms correspondants pour votre événement. (
SomeEvent
,EventSource
etSomeEventEventArgs
)Définissez la visibilité (publique/interne/privée) de la classe de gestionnaire d’événements faibles en accord avec celle de l’événement qu’elle gère.
Utilisez le nouveau gestionnaire d’événements faible au lieu de la liaison d’événements normale.
Par exemple, si votre code utilise le modèle suivant pour s’abonner à un événement :
source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
Remplacez-le par le modèle suivant :
SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
De même, si votre code utilise le modèle suivant pour se désabonner d’un événement :
source.SomeEvent -= new SomeEventEventHandler(OnSome);
Remplacez-le par le modèle suivant :
SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
Voir aussi
- WeakEventManager
- IWeakEventListener
- Vue d’ensemble des événements routés
- Vue d’ensemble de la liaison de données
.NET Desktop feedback