112 votes

Modèle C # pour empêcher un gestionnaire d'événement accroché deux fois

Doublon de: Comment faire pour assurer un événement n'est souscrit pour une fois et Dispose d'un gestionnaire d'événements déjà été ajouté?

J'ai un singleton qui fournit quelques services et mes cours de crochet dans certains événements sur, parfois, une classe est d'accrocher deux fois à l'événement, puis est appelée deux fois. Je suis à la recherche d'un classique de façon à empêcher que cela se produise. en quelque sorte j'ai besoin de vérifier si je l'ai déjà accroché à cet événement...

202voto

PrimeTSS Points 830

Pourquoi ne pas supprimer d'abord l'événement avec -= , s'il n'est pas trouvé, une exception n'est pas levée

 /// -= Removes the event if it has been already added, this prevents multiple firing of the event
((System.Windows.Forms.WebBrowser)sender).Document.Click -= new System.Windows.Forms.HtmlElementEventHandler(testii);
((System.Windows.Forms.WebBrowser)sender).Document.Click += new System.Windows.Forms.HtmlElementEventHandler(testii);
 

162voto

Judah Himango Points 27365

Implémentez explicitement l'événement et vérifiez la liste d'appels. Vous devrez également vérifier la valeur null:

 using System.Linq; // Required for the .Contains call below:

...

private EventHandler foo;
public event EventHandler Foo
{
    add
    {
        if (foo == null || !foo.GetInvocationList().Contains(value))
        {
            foo += value;
        }
    }
    remove
    {
        foo -= value;
    }
}
 

En utilisant le code ci-dessus, si un appelant s’abonne à l’événement plusieurs fois, il sera simplement ignoré.

49voto

LoxLox Points 79

J'ai testé chaque solution et la meilleure (en termes de performances) est:

 private EventHandler _foo;
public event EventHandler Foo {

    add {
        _foo -= value;
        _foo += value;
    }
    remove {
        _foo -= value;
    }
}
 

Aucune utilisation de Linq requise. Il n'est pas nécessaire de rechercher la valeur null avant d'annuler un abonnement (voir MS EventHandler pour plus de détails). Pas besoin de penser à faire la désinscription partout.

21voto

JP Alioto Points 33482

Vous devriez vraiment gérer cela à l'évier et non pas le niveau de la source. Comme le développeur de un service, qui êtes-vous pour dire que le puits ne peut enregistrer qu'une seule fois? Que faire si ils veulent s'inscrire deux fois pour une raison quelconque? Et si vous essayez de corriger des bugs dans les éviers en modifiant la source, c'est encore une bonne raison pour la correction de ces problèmes à l'évier. Je suis sûr que vous avez vos raisons, mais peut-être vous devriez envisager un autre architecture qui laisse la sémantique d'un événement dans le tact.

14voto

Lasse V. Karlsen Points 148037

Vous avez besoin pour mettre en œuvre les ajouter et supprimer des accesseurs sur l'événement, puis vérifier la liste des cibles du délégué, ou de stocker les cibles dans une liste.

Dans la méthode add, vous pouvez utiliser le Délégué.GetInvocationList méthode pour obtenir une liste des cibles déjà ajouté le délégué.

Depuis les délégués sont définis pour comparer l'égalité s'ils sont liés à la même méthode sur le même objet cible, vous pourriez probablement courir à travers la liste et de les comparer, et si vous trouvez aucun qui compare l'égalité, vous ajoutez la nouvelle.

Voici un exemple de code, le compiler comme l'application de la console:

using System;
using System.Linq;

namespace DemoApp
{
    public class TestClass
    {
        private EventHandler _Test;

        public event EventHandler Test
        {
            add
            {
                if (_Test == null || !_Test.GetInvocationList().Contains(value))
                    _Test += value;
            }

            remove
            {
                _Test -= value;
            }
        }

        public void OnTest()
        {
            if (_Test != null)
                _Test(this, EventArgs.Empty);
        }
    }

    class Program
    {
        static void Main()
        {
            TestClass tc = new TestClass();
            tc.Test += tc_Test;
            tc.Test += tc_Test;
            tc.OnTest();
            Console.In.ReadLine();
        }

        static void tc_Test(object sender, EventArgs e)
        {
            Console.Out.WriteLine("tc_Test called");
        }
    }
}

Sortie:

tc_Test called

(ie. une seule fois)

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