Dans votre cas, tout va bien. C'est l'objet qui publie les événements qui maintient en direct les cibles des gestionnaires d'événements. Donc si j'ai:
publisher.SomeEvent += target.DoSomething;
a alors publisher
une référence àtarget
mais pas l'inverse.
Dans votre cas, l'éditeur sera éligible pour le garbage collection (en supposant qu'il n'y ait pas d'autres références à celui-ci), donc le fait qu'il ait une référence aux cibles du gestionnaire d'événements n'est pas pertinent.
Le cas délicat est celui où l'éditeur a une longue durée de vie mais que les abonnés ne veulent pas l'être - dans ce cas, vous devez désabonner les gestionnaires. Par exemple, supposons que vous disposiez d'un service de transfert de données qui vous permette de vous abonner à des notifications asynchrones sur les modifications de bande passante et que l'objet de service de transfert a une longue durée de vie. Si nous faisons ceci:
BandwidthUI ui = new BandwidthUI();
transferService.BandwidthChanged += ui.HandleBandwidthChange;
// Suppose this blocks until the transfer is complete
transferService.Transfer(source, destination);
// We now have to unsusbcribe from the event
transferService.BandwidthChanged -= ui.HandleBandwidthChange;
(Vous voudriez en fait utiliser un bloc finally pour vous assurer de ne pas divulguer le gestionnaire d'événements.) Si nous ne nous désinscrivons pas, alors le service BandwidthUI
vivrait au moins aussi longtemps que le service de transfert.
Personnellement, je rencontre rarement cela - généralement si je m'abonne à un événement, la cible de cet événement vit au moins aussi longtemps que l'éditeur - un formulaire durera aussi longtemps que le bouton qui se trouve dessus, par exemple. Cela vaut la peine de connaître ce problème potentiel, mais je pense que certaines personnes s'en inquiètent quand elles n'en ont pas besoin, car elles ne savent pas dans quel sens vont les références.
EDIT: C'est pour répondre au commentaire de Jonathan Dickinson. Tout d'abord, regardez la documentation pour Delegate.Equals (object) qui donne clairement le comportement d'égalité.
Deuxièmement, voici un programme court mais complet pour montrer le fonctionnement de la désinscription:
using System;
public class Publisher
{
public event EventHandler Foo;
public void RaiseFoo()
{
Console.WriteLine("Raising Foo");
EventHandler handler = Foo;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
else
{
Console.WriteLine("No handlers");
}
}
}
public class Subscriber
{
public void FooHandler(object sender, EventArgs e)
{
Console.WriteLine("Subscriber.FooHandler()");
}
}
public class Test
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
publisher.Foo += subscriber.FooHandler;
publisher.RaiseFoo();
publisher.Foo -= subscriber.FooHandler;
publisher.RaiseFoo();
}
}
Résultats:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Testé sur Mono et .NET 3.5SP1.)
Modifier davantage:
Il s'agit de prouver qu'un éditeur d'événement peut être collecté tant qu'il existe encore des références à un abonné.
using System;
public class Publisher
{
~Publisher()
{
Console.WriteLine("~Publisher");
Console.WriteLine("Foo==null ? {0}", Foo == null);
}
public event EventHandler Foo;
}
public class Subscriber
{
~Subscriber()
{
Console.WriteLine("~Subscriber");
}
public void FooHandler(object sender, EventArgs e) {}
}
public class Test
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
publisher.Foo += subscriber.FooHandler;
Console.WriteLine("No more refs to publisher, "
+ "but subscriber is alive");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End of Main method. Subscriber is about to "
+ "become eligible for collection");
GC.KeepAlive(subscriber);
}
}
Résultats (dans .NET 3.5SP1; Mono semble se comporter un peu bizarrement ici. Nous examinerons cela quelque temps):
No more refs to publisher, but subscriber is alive
~Publisher
Foo==null ? False
End of Main method. Subscriber is about to become eligible for collection
~Subscriber
Dans votre cas, vous allez bien. Au départ, j'ai lu votre question à l'envers, à savoir qu'un abonné sortait du cadre, pas l' éditeur . Si l'éditeur d'événements sort du champ d'application, les références à l'abonné (et non à l'abonné lui-même, bien sûr!) L'accompagnent et il n'est pas nécessaire de les supprimer explicitement.
Ma réponse originale est ci-dessous, à propos de ce qui se passe si vous créez un abonné à un événement et le laissez sortir de la portée sans vous désabonner. Cela ne s'applique pas à votre question, mais je vais la laisser en place pour l'histoire.
Si la classe est toujours enregistrée via des gestionnaires d'événements, elle est toujours accessible. C'est toujours un objet vivant. Un GC suivant un graphe d'événements le trouvera connecté. Oui, vous souhaiterez supprimer explicitement les gestionnaires d'événements.
Ce n'est pas parce que l'objet est hors de portée de son allocation d'origine qu'il est candidat au GC. Tant qu'une référence en direct demeure, elle est en direct.
la source