123 votes

Est-il nécessaire de supprimer explicitement les gestionnaires d'événements en C #

J'ai une classe qui offre un certain nombre d'événements. Cette classe est déclarée à l'échelle mondiale, mais pas instanciées sur la déclaration mondiale--c'est instanciée sur une base comme-nécessaire dans les méthodes qui en ont besoin.

Chaque fois que la classe est nécessaire dans une méthode, il est instanciée et les gestionnaires d'événements sont enregistrés. Est-il nécessaire de supprimer les gestionnaires d'événements explicitement avant la méthode est hors de portée?

Lorsque la méthode est hors de portée, ainsi va l'instance de la classe. Ne laissant gestionnaires d'événements enregistrés auprès de cette instance qui est hors de portée ont une empreinte mémoire implication? (Je me demande si le gestionnaire d'événement garde la GC de voir l'instance de classe comme n'est plus référencé.)

Je vous remercie.

184voto

Jon Skeet Points 692016

Dans votre cas, tout va bien. C'est l'objet qui publie les événements qui maintient les objectifs des gestionnaires d'événements en direct. Donc, si j'ai:

publisher.SomeEvent += target.DoSomething;

ensuite, publisher a une référence à l' target mais pas l'inverse.

Dans votre cas, l'éditeur va être admissibles pour la collecte des ordures (en supposant que il n'y a pas d'autres références), de sorte que le fait qu'il y ait une référence pour le gestionnaire d'événements cibles n'est pas pertinent.

Le cas délicat, c'est quand l'éditeur est de longue durée, mais les abonnés ne veux pas être - dans que cas vous devez vous désabonner les gestionnaires. Par exemple, supposons que vous ayez un service de transmission de données qui vous permet de vous abonner aux notifications asynchrones de la bande passante des changements, et le service de transfert de l'objet est de longue durée. Si nous faisons cela:

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 auriez à utiliser un bloc finally pour vous assurer de ne pas laisser couler le gestionnaire d'événements). Si nous n'avons pas de désabonnement, puis l' BandwidthUI vivraient au moins aussi longtemps que le service de transfert.

Personnellement, j'ai rarement rencontré ce - habituellement, si je m'abonner à un événement, la cible de cet événement de vie au moins aussi longtemps que l'éditeur - une forme durera aussi longtemps que le bouton qui est sur elle, par exemple. Il faut savoir à propos de ce problème potentiel, mais je pense que certaines personnes s'en inquiéter quand ils n'ont pas besoin, car ils ne savent pas qui, autour de la référence.

EDIT: C'est pour répondre à Jonathan Dickinson commentaire. Tout d'abord, regarde la doc de Délégué.Equals(object) qui donnent clairement l'égalité de comportement.

Deuxièmement, voici une courte mais complète du programme est de montrer l'annulation de l'abonnement de travail:

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 du Mono et du .NET 3.5SP1.)

Modifier:

C'est pour prouver que l'éditeur d'événement peuvent être collectées bien qu'il existe encore des références à un abonné.

using System;

public class Publisher
{
    ~Publisher()
    {
        Console.WriteLine("~Publisher");
    }

    public event EventHandler Foo;
}

public class Subscriber
{
    ~Subscriber()
    {
        Console.WriteLine("~Subscriber");
        Console.WriteLine("Foo==null ? {0}", Foo == null);
    }

    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);
    }
}

Les résultats de la .NET 3.5SP1; Mono semble se comporter légèrement bizarrement ici. Se regarder dans un intervalle de 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

8voto

Eddie Points 27755

Dans votre cas, vous êtes beaux. J'ai d'abord lu votre question à l'envers, qu'un abonné a été de sortir de la portée, non pas l' éditeur. Si l'éditeur d'événement est hors de portée, puis les références à l'abonné (pas l'abonné lui-même, bien sûr!) aller avec elle et il n'est pas nécessaire de supprimer explicitement.

Ma réponse est ci-dessous, à propos de ce qui se passe si vous créez un événement de l'abonné et le laisser aller hors de portée sans vous désabonner. Elle ne s'applique pas à votre question mais je vais la laisser en place l'histoire.

Si la classe est toujours enregistré via des gestionnaires d'événements, il est toujours accessible. Il est encore un objet live. Un GC à la suite d'un événement graphique trouverez qu'il est connecté. Oui, vous voulez supprimer explicitement les gestionnaires d'événements.

Tout simplement parce que l'objet est hors de portée de la dotation initiale ne signifie pas qu'il est un candidat pour la GC. Aussi longtemps qu'un live de référence reste, c'est vivre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X