Dois-je supprimer les abonnements aux événements d’objects avant qu’ils ne deviennent orphelins?

Si mon logiciel a deux instances d’object, dont l’une est abonnée aux événements de l’autre. Dois-je les désabonner les uns des autres avant qu’ils ne soient orphelins pour pouvoir être nettoyés par le ramasse-miettes? Ou y a-t-il une autre raison pour laquelle je devrais effacer les relations d’événement? Que se passe-t-il si l’object souscrit est orphelin mais que l’abonné ne l’est pas ou vice versa?

Oui vous faites. Les éditeurs d’événements conservent des références aux objects et les empêcheraient d’être récupérés.

Regardons un exemple pour voir ce qui se passe. Nous avons deux classes; l’un expose un événement, l’autre le consum:

class ClassA { public event EventHandler Test; ~ClassA() { Console.WriteLine("A being collected"); } } class ClassB { public ClassB(ClassA instance) { instance.Test += new EventHandler(instance_Test); } ~ClassB() { Console.WriteLine("B being collected"); } void instance_Test(object sender, EventArgs e) { // this space is intentionally left blank } } 

Notez que ClassB ne stocke pas de référence à l’instance ClassA. il ne fait que connecter un gestionnaire d’événements.

Voyons maintenant comment les objects sont collectés. Scénario 1:

 ClassB temp = new ClassB(new ClassA()); Console.WriteLine("Collect 1"); GC.Collect(); Console.ReadKey(); temp = null; Console.WriteLine("Collect 2"); GC.Collect(); Console.ReadKey(); 

Nous créons une instance ClassB et maintenons une référence à celle-ci par le biais de la variable temp. Elle se voit transmettre une nouvelle instance de ClassA, dans laquelle nous ne stockons aucune référence à cette référence. Elle sort donc de sa scope immédiatement après la fin du constructeur de ClassB. Le ramasse-miettes est exécuté une fois lorsque ClassA est hors de scope et une fois lorsque ClassB est devenu hors de scope. Le résultat:

 Collect 1 A being collected Collect 2 B being collected 

Scénario 2:

 ClassA temp = new ClassA(); ClassB temp2 = new ClassB(temp); temp2 = null; Console.WriteLine("Collect 1"); GC.Collect(); Console.ReadKey(); temp = null; Console.WriteLine("Collect 2"); GC.Collect(); Console.ReadKey(); 

Une nouvelle instance de ClassA est créée et une référence à celle-ci est stockée dans la variable temp. Ensuite, une nouvelle instance de ClassB est créée, qui lui transmet l’instance ClassA en temp et nous stockons une référence dans temp2. Ensuite, nous avons défini temp2 sur null, ce qui a pour effet de rendre l’instance ClassB inutilisable. Comme auparavant, le ramasse-miettes est exécuté une fois que chaque instance est hors de scope. Le résultat:

 Collect 1 Collect 2 B being collected A being collected 

Donc, pour conclure; si l’instance qui expose un événement sort de son périmètre, elle devient disponible pour le nettoyage de la mémoire, qu’il y ait ou non des gestionnaires d’événements connectés. Si une instance ayant un gestionnaire d’événements lié à un événement d’une autre instance, elle ne sera pas disponible pour le garbage collection tant que le gestionnaire d’événements ne sera pas détaché, ou que l’instance à laquelle le gestionnaire d’événements est attaché deviendra disponible pour le garbage collection.

Vous n’avez besoin de décrocher des événements que si l’object qui les expose a une durée de vie longue, mais l’object qui l’ accroche à l’événement serait sinon de courte durée (et les ordures récupérées assez rapidement).

Dans ce cas, ne pas décrocher causera ce qui équivaut à une fuite de mémoire, car votre object de courte durée ne pourra pas être ignoré (GCed), car l’événement de l’object de longue durée conserve un délégué, qui contient une référence à l’object éphémère. Étant donné que l’object de courte durée est toujours référencé par ce délégué, il ne peut pas être récupéré.

Les événements statiques ont par définition une longue durée de vie – ils survivent jusqu’à la fin du programme. Si vous décrochez un événement statique, vous devez absolument le décrocher lorsque vous avez terminé.

Si les deux objects sont sur le point d’être orphelins, le décrochage n’est pas nécessaire.

L’abonnement à un événement entraîne une forte référence à l’abonné. En effet, sous les couvertures, les événements sont des delegates et les delegates des méthodes d’instance sont une combinaison de la référence d’object et de la méthode réelle. Si vous ne vous désabonnez pas, l’éditeur continue à gérer les références et les objects abonnés ne deviennent jamais véritablement orphelins (et GC ‘) tant que l’éditeur est actif.

L’inverse n’est pas vrai, c’est-à-dire que l’object souscrit n’a aucune référence à l’éditeur.