51 votes

Utilisation de IDisposable pour désinscrire des événements

J'ai une classe qui gère les événements d'un contrôle WinForms. En fonction de ce que fait l'utilisateur, je déferre une instance de la classe et en crée une nouvelle pour gérer le même événement. Je dois d'abord désinscrire l'ancienne instance de l'événement - assez facile. J'aimerais faire cela d'une manière non propriétaire si possible, et il semble que ce soit un travail pour IDisposable. Cependant, la plupart des documents recommandent l'utilisation d'IDisposable uniquement en cas d'utilisation de ressources non gérées, ce qui n'est pas le cas ici.

Si j'implémente IDisposable et que je me désinscris de l'événement dans Dispose(), est-ce que je pervertis son intention ? Devrais-je plutôt fournir une fonction Unsubscribe() et l'appeler ?


Editar: Voici un code fictif qui montre un peu ce que je fais (en utilisant IDisposable). Mon implémentation réelle est liée à une liaison de données propriétaire (longue histoire).

class EventListener : IDisposable
{
    private TextBox m_textBox;

    public EventListener(TextBox textBox)
    {
        m_textBox = textBox;
        textBox.TextChanged += new EventHandler(textBox_TextChanged);
    }

    void textBox_TextChanged(object sender, EventArgs e)
    {
        // do something
    }

    public void Dispose()
    {
        m_textBox.TextChanged -= new EventHandler(textBox_TextChanged);
    }
}

class MyClass
{
    EventListener m_eventListener = null;
    TextBox m_textBox = new TextBox();

    void SetEventListener()
    {
        if (m_eventListener != null) m_eventListener.Dispose();
        m_eventListener = new EventListener(m_textBox);
    }
}

Dans le code réel, la classe "EventListener" est plus impliquée, et chaque instance a une signification unique. Je les utilise dans une collection, et les crée/détruit au fur et à mesure que l'utilisateur clique.


Conclusion

J'accepte La réponse de gbjbaanb du moins pour l'instant. J'ai le sentiment que l'avantage d'utiliser une interface familière l'emporte sur les inconvénients éventuels de son utilisation lorsqu'aucun code non géré n'est impliqué (comment un utilisateur de cet objet pourrait-il même le savoir ?).

Si quelqu'un n'est pas d'accord - merci de poster/commenter/modifier. Si un meilleur argument peut être avancé contre IDisposable, alors je changerai la réponse acceptée.

42voto

gbjbaanb Points 31045

Oui, allez-y. Bien que certaines personnes pensent qu'IDisposable n'est implémenté que pour les ressources non gérées, ce n'est pas le cas - les ressources non gérées se trouvent être le plus grand gain, et la raison la plus évidente pour l'implémenter. Je pense qu'il a acquis cette idée parce que les gens ne pouvaient pas penser à une autre raison de l'utiliser. Ce n'est pas comme un finisseur qui est un problème de performance et qui n'est pas facile à gérer par la GC.

Mettez tout code de rangement dans votre méthode de disposition. Ce sera plus clair, plus propre et beaucoup plus susceptible d'empêcher les fuites de mémoire et beaucoup plus facile à utiliser correctement que d'essayer de se rappeler de supprimer vos références.

L'intention d'IDisposable est d'améliorer le fonctionnement de votre code sans que vous ayez à faire beaucoup de travail manuel. Utilisez son pouvoir en votre faveur et dépassez les absurdités artificielles des "intentions de conception".

Je me souviens qu'il a été assez difficile de persuader Microsoft de l'utilité de la finalisation déterministe lors de la sortie de .NET - nous avons gagné la bataille et les avons persuadés de l'ajouter (même si ce n'était qu'un modèle de conception à l'époque), utilisez-le !

13voto

JaredPar Points 333733

Mon avis personnel serait d'avoir une méthode de désinscription afin de retirer la classe des événements. IDisposable est un pattern destiné à la libération déterministe de ressources non gérées. Dans ce cas, vous ne gérez pas de ressources non gérées et ne devriez donc pas implémenter IDisposable.

IDisposable peut être utilisé pour gérer les abonnements aux événements mais ne devrait probablement pas l'être. Pour un exemple, je vous renvoie à WPF. Il s'agit d'une bibliothèque qui regorge d'événements et de gestionnaires d'événements. Pourtant, pratiquement aucune classe de WPF n'implémente IDisposable. J'en déduirais que les événements doivent être gérés d'une autre manière.

9voto

VitalyB Points 2318

Une chose qui me dérange dans l'utilisation IDisposable Le modèle de désabonnement aux événements est un problème de finalisation.

Dispose() fonction dans IDisposable est censée être appelée par le développeur, MAIS, si elle n'est pas appelée par le développeur, il est entendu que le GC appellera cette fonction (par la norme IDisposable du moins). Dans votre cas, cependant, si vous n'appelez pas Dispose personne d'autre ne le fera - L'événement reste et la référence forte empêche GC d'appeler le finisseur.

Le simple fait que Dispose () ne sera pas appelé automatiquement par GC me semble suffisant pour ne pas utiliser IDisposable dans ce cas. Peut-être qu'il faut une nouvelle interface spécifique à l'application qui dit que ce type d'objet doit avoir un objet de type Nettoyage appelé pour être éliminé par le GC.

5voto

Jason Coyne Points 3593

Je pense que le jetable est pour tout ce que la GC ne peut pas prendre en charge automatiquement, et les références aux événements comptent dans mon livre. Voici une classe d'aide que j'ai créée.

public class DisposableEvent<T> : IDisposable
    {

        EventHandler<EventArgs<T>> Target { get; set; }
        public T Args { get; set; }
        bool fired = false;

        public DisposableEvent(EventHandler<EventArgs<T>> target)
        {
            Target = target;
            Target += new EventHandler<EventArgs<T>>(subscriber);
        }

        public bool Wait(int howLongSeconds)
        {
            DateTime start = DateTime.Now;
            while (!fired && (DateTime.Now - start).TotalSeconds < howLongSeconds)
            {
                Thread.Sleep(100);
            }
            return fired;
        }

        void subscriber(object sender, EventArgs<T> e)
        {
            Args = e.Value;
            fired = true;
        }

        public void Dispose()
        {
            Target -= subscriber;
            Target = null;
        }

    }

qui vous permet d'écrire ce code :

Class1 class1 = new Class1();
            using (var x = new DisposableEvent<object>(class1.Test))
            {
                if (x.Wait(30))
                {
                    var result = x.Args;
                }
            }

Un effet secondaire, vous ne devez pas utiliser le mot clé event sur vos événements, car cela empêche de les passer comme paramètre au constructeur de l'aide, cependant, cela ne semble pas avoir d'effets néfastes.

3voto

annakata Points 42676

IDisposable est fermement lié aux ressources, et la source de suffisamment de problèmes pour ne pas brouiller les pistes davantage je pense.

Je vote aussi pour une méthode de désabonnement sur votre propre interface.

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