2010-08-06 1 views
2

Si j'ai une classe avec un couple de gestionnaires d'événements qui sont attachés à un objet qui est défini dans la classe, ai-je raison penser que je n'ai pas besoin d'implémenter IDisposable pour supprimer ces gestionnaires, même si j'ai ajouté explicitement les gestionnaires moi-même?Précisions sur le moment où je dois supprimer des gestionnaires dans .Net, VB.Net

Aussi, quelqu'un peut-il me diriger dans la direction d'un article qui explique la portée de quand il est nécessaire de supprimer les gestionnaires pour éviter les fuites de mémoire. (J'ai essayé de chercher plusieurs fois, mais je dois foutre en l'air mes termes de recherche.)

ETA: Voici une situation où je pense que j'ai besoin de retirer les gestionnaires. Corrigez-moi si j'ai tort, s'il-vous plait.

J'ai une collection; Chaque fois qu'un objet est ajouté à cette collection, j'ajoute un gestionnaire à un événement de changement de l'objet. Lorsque j'en ai fini avec la collection, dois-je passer en revue chaque élément et supprimer le gestionnaire avant de définir la référence sur null?

Répondre

2

Vous souhaitez supprimer les gestionnaires si l'objet conteneur est conservé dans les limbes après que toutes les références dans votre application ont été supprimées.

Par exemple ...

public class LostInLimbo 
{ 
    private Villain _problem; 

    public void SetVillain(Villain problem) 
    { 
    problem.SomeEvent += this.SomeHandler; 
    _problem = problem; 
    } 
} 

Compte tenu de cette classe, si nous faisons ce qui suit:

Villain foo = new Villain(); 
LostInLimbo victim = new LostInLimbo(); 
victim.SetVillain(foo); 
victim = null; 

l'instance victim est maintenant "fuite". Il est référencé par foo et ne sera donc pas collecté; cependant, vous ne pouvez pas accéder à cette instance. Faites-le quelques centaines de milliers de fois et vous pourriez avoir un problème.

Dans ce cas, vous voulez LostInLimbo à mettre en œuvre IDisposable, où vous pouvez décrochez l'événement:

var temp = _problem; 
_problem = null; 
if (temp != null_ 
    temp -= this.SomeHandler; 

De même, vous pourriez vous retrouver avec une fuite si vous faites ceci:

public class LostInLimbo 
{ 
    public Villain Problem {get;private set;} 

    public LostInLimbo() 
    { 
    Problem = new Villain(); 
    Problem.SomeEvent += this.SomeHandler; 
    } 
} 

et effectuer les opérations suivantes

var temp = new LostInLimbo().Villain; 

Même situation. Villain contient une référence à l'instance de LostInLimbo, mais vous n'avez pas accès à cette instance. Encore une fois, l'implémentation de IDisposable permettra à l'instance de LostInLimbo de se décrocher elle-même. Bien sûr, il y a d'autres problèmes avec cela, mais ce n'est qu'un exemple. Cependant, vous avez

Si cette situation:

public class LostInLimbo 
{ 
    private Villain _problem; 
    public LostInLimbo() 
    { 
    _problem = new Villain(); 
    _problem.SomeEvent += this.SomeHandler; 
    } 
} 

il n'y a pas de soucis. Seul LostInLimbo contient une référence à _problem, et _problem ne contient aucune référence à une instance de LostInLimbo à l'exception de celle qui la "possède". Une fois cette instance collectée, il en est de même de _problem.


En réponse à votre mise à jour, je voudrais procéder comme suit. Tout d'abord, lorsqu'un élément est ajouté à la collection, la collection se connecte à l'élément (remplace toute méthode qui ajoute un élément à la collection). Lorsqu'un élément est supprimé de la collection, la collection se déconnecte de l'élément (à nouveau, remplacez toute méthode qui supprime un élément de la collection). Je voudrais également implémenter IDisposable pour vider la collection sur disposer. Je m'assurerais que tout type qui utilise la collection implémente IDisposable.

+0

Comme vous l'avez expliqué, c'est exactement ce que je fais en ce moment, cependant, à la lecture de votre réponse, il ne semble pas que je doive vider la collection quand j'en ai fini, tant que j'ai défini la référence à la collection à null. Est-ce juste pour l'exhaustivité? – Jules

+0

@Jules Je le fais habituellement, pour être complet. Si je me débarrasse d'une collection, j'obtiendrai tous les articles de la collection, j'éclaircirai la collection, puis je forgerai ce que j'ai et je m'en débarrasserai. Juste mon habitude particulière, et il est plus facile d'avoir une méthode Dispose que vous pouvez appeler plus d'une fois sans lancer une exception. – Will

1

Cela dépend vraiment -

En règle générale, il suffit de vous soucier de la suppression des gestionnaires d'événements si l'abonné va survivre votre objet (dans ce cas, à défaut d'enlever empêchera votre objet d'être des déchets collectés), ou s'il y a une chance que votre événement soit déclenché, et vous ne voulez pas que les abonnés continuent le traitement.

Si l'objet abonné à l'événement est interne à votre classe, vous n'avez pas besoin de vous désabonner/supprimer le gestionnaire. Dans ce cas, lorsque votre objet est non raclé, les deux objets seront collectés (puisqu'ils seront tous les deux déconnectés, à condition qu'aucune autre référence n'existe).

+0

C'est ce que je pensais. Je viens d'ajouter un peu à ma question qui semble correspondre au reste de ce que vous avez dit. Pourriez-vous vérifier s'il vous plaît pour moi s'il vous plaît. – Jules

+0

En fait, peut-être pas. L'abonné dans mon cas serait la collection, et il ne survivrait pas à l'objet parce que j'aurais mis sa seule référence à null. – Jules

+0

@Jules: Tant que rien d'autre ne contient de références aux objets de votre collection (ie: ils sont SEULEMENT dans la collection), vous n'avez même pas besoin de les mettre à null - dès que la collection est non racinée, tout sera (éventuellement) obtenu GC'ed. –

1

IDisposable n'a rien à voir avec les événements, il a été conçu pour libérer des ressources non managées. En règle générale, vous ne rencontrez un problème avec un gestionnaire d'événements qui empêche un objet d'être collecté si la source de l'événement survit au consommateur d'événement. On dirait, dans votre cas, qu'ils seront tous deux collectés en même temps, pas besoin de désinscrire l'événement. Toutefois, si la source d'événements survit toujours au consommateur, vous avez un problème. Vous aurez besoin d'un déclencheur pour désenregistrer le gestionnaire d'événements. Si le consommateur implémente déjà Dispose() alors cela pourrait être l'endroit où désinscrire le gestionnaire. Si ce n'est pas le cas, vous devriez vraiment envisager d'utiliser une méthode de rappel simple au lieu d'un événement.

+0

"si la source de l'événement dépasse le consommateur de l'événement". Est-ce que cette ligne est correcte ou est-ce que vous vouliez dire l'inverse? Il semble être le contraire de la ligne de Reed "si l'abonné va survivre à votre objet" – Jules

+0

Non, c'est correct. Un abonné ne peut pas survivre à la source, le gestionnaire d'événements enregistrés empêchera la source de récupérer les données collectées. Le gestionnaire arrêtera juste d'être appelé. –

Questions connexes