2008-08-19 20 views
19

Disons que nous avons la méthode suivante:L'utilisation de lambdas comme gestionnaire d'événements peut-elle provoquer une fuite de mémoire?

private MyObject foo = new MyObject(); 

// and later in the class 

public void PotentialMemoryLeaker(){ 
    int firedCount = 0; 
    foo.AnEvent += (o,e) => { firedCount++;Console.Write(firedCount);}; 
    foo.MethodThatFiresAnEvent(); 
} 

Si la classe avec cette méthode est instancié et la méthode PotentialMemoryLeaker est appelée plusieurs fois, avons-nous une fuite de mémoire?

Y at-il un moyen de décrocher ce gestionnaire d'événements lambda après que nous ayons fini d'appeler MethodThatFiresAnEvent?

+0

Comme indiqué dans les réponses ci-dessous, il n'y a pas moyen de le décrochez sans conserver une référence. Cependant, vous pouvez le faire décrocher: http://stackoverflow.com/questions/1747235/weak-event-handler-model-for-use-with-lambdas/1747236#1747236 – Benjol

Répondre

16

Oui, enregistrez-le dans une variable et décrochez-le.

DelegateType evt = (o, e) => { firedCount++; Console.Write(firedCount); }; 
foo.AnEvent += evt; 
foo.MethodThatFiresAnEvent(); 
foo.AnEvent -= evt; 

Et oui, si vous ne le faites pas, vous des fuites mémoire, comme vous accrochez un nouvel objet délégué à chaque fois. Vous remarquerez aussi ceci parce que chaque fois que vous appelez cette méthode, il va déverser sur la console un nombre croissant de lignes (pas seulement un nombre croissant, mais pour un appel à MethodThatFiresAnEvent, il va vider un certain nombre d'éléments, une fois pour chaque méthode anonyme connectée).

0

Oui de la même manière que les gestionnaires d'événements normaux peuvent provoquer des fuites. Parce que le lambda est réellement changé à:

someobject.SomeEvent +=() => ...; 
someobject.SomeEvent += delegate() { 
    ... 
}; 

// unhook 
Action del =() => ...; 
someobject.SomeEvent += del; 
someobject.SomeEvent -= del; 

Donc, fondamentalement, il est juste avant la main pour ce que nous utilisons dans 2.0 toutes ces années.

4

Vous ne manquerez pas de mémoire, vous obtiendrez également votre lambda appelé plusieurs fois. Chaque appel de 'PotentialMemoryLeaker' ajoutera une autre copie de lambda à la liste des événements, et chaque copie sera appelée lorsque 'AnEvent' sera renvoyé.

1

Votre exemple se contente de compiler une classe interne privée nommée par le compilateur (avec field firedCount et une méthode nommée par le compilateur). Chaque appel à PotentialMemoryLeaker crée une nouvelle instance de la classe de fermeture à laquelle foo conserve une référence au moyen d'un délégué à la méthode unique.

Si vous ne référencez pas l'objet entier qui possède PotentialMemoryLeaker, alors tout cela sera collecté. Dans le cas contraire, vous pouvez définir foo null ou la liste de gestionnaire d'événements de foo vide en écrivant ceci:

foreach (var handler in AnEvent.GetInvocationList()) AnEvent -= handler; 

Bien sûr, vous aurez besoin d'accès aux membres privés de la MyObject classe.

3

Eh bien, vous pouvez prolonger ce qui a été fait pour rendre les délégués here plus sûrs à utiliser (pas de fuites de mémoire)

+1

le lien est mort – thumbmunkeys