web-dev-qa-db-fra.com

Dois-je me désinscrire des événements?

J'ai 3 questions concernant les événements:

  1. Dois-je toujours me désinscrire des événements qui ont été abonnés?
  2. Que se passe-t-il si je ne le fais PAS?
  3. Dans les exemples ci-dessous, comment vous désinscrire des événements auxquels vous êtes abonné?

J'ai par exemple ce code:

Ctor: Objectif: pour les mises à jour des propriétés de la base de données

this.PropertyChanged += (o, e) =>
{
    switch (e.PropertyName)
    {
        case "FirstName": break;
        case "LastName": break;
    }
};

et ceci: Objectif: pour une liaison à l'interface graphique, enveloppez le modèle dans des modèles de vue

ObservableCollection<Period> periods = _lpRepo.GetDailyLessonPlanner(data.DailyDate);
PeriodListViewModel = new ObservableCollection<PeriodViewModel>();

foreach (Period period in periods)
{
    PeriodViewModel periodViewModel = new PeriodViewModel(period,_lpRepo);
    foreach (DocumentListViewModel documentListViewModel in periodViewModel.DocumentViewModelList)
    {
        documentListViewModel.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument);
        documentListViewModel.AddDocumentDelegate += new Action(OnAddDocument);
        documentListViewModel.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument);
    }
    PeriodListViewModel.Add(periodViewModel);
}
41
Elisabeth

1) Cela dépend. C'est généralement une bonne idée, mais il y a des cas typiques où vous n'en avez pas besoin. Fondamentalement, si vous êtes sûr que l'objet abonné va survivre à la source d'événement, vous devez vous désinscrire, sinon cela créerait une référence inutile.

Si toutefois votre objet souscrit à ses propres événements, comme dans l'exemple suivant:

<Window Loaded="self_Loaded" ...>...</Window>

- alors vous n'avez pas à le faire.

2) L'abonnement à un événement fait une référence supplémentaire à l'objet abonné. Donc, si vous ne vous désabonnez pas, votre objet peut être maintenu en vie par cette référence, ce qui entraîne une fuite de mémoire. En vous désabonnant, vous supprimez cette référence. Notez que dans le cas de l'auto-abonnement, le problème ne se pose pas.

3) Vous pouvez faire comme ça:

this.PropertyChanged += PropertyChangedHandler;
...
this.PropertyChanged -= PropertyChangedHandler;

void PropertyChangedHandler(object o, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "FirstName": break;
        case "LastName": break;
    }
}
28
Vlad

Eh bien, prenons d'abord la dernière question. Vous ne pouvez pas vous désabonner de manière fiable d'un événement auquel vous êtes abonné directement avec une expression lambda. Vous soit devez conserver une variable avec le délégué (vous pouvez donc toujours utiliser une expression lambda) o vous devez utiliser une conversion de groupe de méthodes à la place.

Quant à savoir si vous devez réellement vous désinscrire, cela dépend de la relation entre le producteur de l'événement et le consommateur de l'événement. Si le producteur d'événements doit vivre plus longtemps que le consommateur d'événements, vous devriez vous désinscrire - sinon le producteur aura une référence au consommateur, le gardant en vie plus longtemps qu'il ne devrait l'être. Le gestionnaire d'événements continuera également à être appelé aussi longtemps que le producteur le produira.

Maintenant, dans de nombreux cas, ce n'est pas un problème - par exemple, dans un formulaire, le bouton qui déclenche l'événement Click est susceptible de durer environ aussi longtemps que le formulaire sur lequel il est créé, où le gestionnaire est généralement abonné. .. il n'est donc pas nécessaire de se désinscrire. Ceci est très typique d'une interface graphique.

De même, si vous créez un WebClient uniquement aux fins d'une seule demande asynchrone, abonnez-vous à l'événement pertinent et démarrez la demande asynchrone, le WebClient lui-même sera éligible pour le garbage collection lorsque la demande a terminé (en supposant que vous ne gardiez pas de référence ailleurs).

Fondamentalement, vous devez toujours considérer la relation entre le producteur et le consommateur. Si le producteur va vivre plus longtemps que vous ne le souhaiteriez, o il continuera à déclencher l'événement après que cela ne vous intéresse plus, alors vous devez vous désinscrire.

55
Jon Skeet

Vous pouvez jeter un oeil à cet article sur MSDN. Citation:

Pour éviter que votre gestionnaire d'événements ne soit appelé lorsque l'événement est déclenché, il vous suffit de vous désabonner de l'événement. Pour éviter les fuites de ressources, il est important de vous désabonner des événements avant de vous débarrasser d'un objet abonné. Jusqu'à ce que vous vous désabonniez d'un événement, le délégué de multidiffusion qui sous-tend l'événement dans l'objet de publication a une référence au délégué qui encapsule le gestionnaire d'événements de l'abonné. Tant que l'objet de publication contient cette référence, votre objet abonné ne sera pas récupéré.

5
Darin Dimitrov

Vous n'avez pas à vous désabonner d'un événement lorsque l'instance qui s'abonne a la même portée que l'instance à laquelle vous êtes abonné.

Disons que vous êtes un formulaire et que vous vous abonnez à un contrôle, ces deux éléments forment un groupe. Cependant, si vous avez une classe centrale qui gère les formulaires et que vous êtes abonné à l'événement Closed de ce formulaire, ceux-ci ne forment pas un groupe ensemble et vous devez vous désinscrire une fois le formulaire fermé.

L'abonnement à un événement fait que l'instance abonnée crée une référence à l'instance à laquelle vous êtes abonné. Cela empêche la collecte des ordures. Ainsi, lorsque vous avez une classe centrale qui gère les instances de formulaire, cela gardera tous les formulaires en mémoire.

WPF est une exception car il a un modèle d'événement faible dans lequel les événements sont abonnés à l'aide de références faibles et il ne conservera pas le formulaire en mémoire. Cependant, il est toujours préférable de vous désinscrire lorsque vous ne faites pas partie du formulaire.

4
Pieter van Ginkel

1.) Dois-je toujours désinscrire des événements qui ont été abonnés?
Généralement oui. La seule exception est lorsque l'objet sur lequel vous vous êtes abonné n'est plus référencé et sera bientôt récupéré.

2.) Que se passe-t-il si je ne le fais PAS?
L'objet sur lequel vous vous êtes abonné contiendra une référence au délégué, qui à son tour contiendra une référence à son pointeur this, et vous obtiendrez ainsi une fuite de mémoire.
Ou si le gestionnaire était un lamda, il conservera toutes les variables locales qu'il a liées, qui ne seront donc pas collectées non plus.

1
CodesInChaos