2008-12-30 2 views
44

Si j'attribue un gestionnaire d'événements au moment de l'exécution et qu'il se trouve dans un emplacement pouvant être appelé plusieurs fois, il est recommandé d'empêcher plusieurs affectations du même gestionnaire au même événement. Ajouter ceci à un endroit qui sera appelé plus d'une fois exécutera le gestionnaire 'n' fois (bien sûr).Empêcher la même affectation de gestionnaire d'événements plusieurs fois

J'ai eu recours à enlever tout gestionnaire précédent avant d'essayer d'ajouter via

object.Event -= MyFunction; 

object.Event += MyFunction; 

Cela fonctionne, mais semble hors d'une certaine manière. Toutes les suggestions sur la bonne manipulation;) de ce scénario.

+1

Votre solution de suppression de l'événement d'abord, puis l'ajout est en fait une réponse élevée à http://stackoverflow.com/questions/937181/c-sharp-pattern-to-prevent-an-event-handler-hooked- deux fois (cette question a aussi plus de réponses à ce sujet) – OneWorld

+0

duplicata possible de [Comment assurer qu'un événement est seulement souscrit à une fois] (http://stackoverflow.com/questions/367523/how-to-ensure-an-event -is-only-abonné-à-une fois) –

Répondre

36

Baget a raison d'utiliser un événement explicitement implémenté (bien qu'il y ait un mélange d'implémentation d'interface explicite et de syntaxe d'événement complète). Vous pouvez probablement avec ceci:

private EventHandler foo; 

public event EventHandler Foo 
{ 
    add 
    { 
     // First try to remove the handler, then re-add it 
     foo -= value; 
     foo += value; 
    } 
    remove 
    { 
     foo -= value; 
    } 
} 

Cela peut avoir des cas limites bizarres si jamais vous ajoutez ou supprimez les délégués multicast, mais qui est peu probable. Il a également besoin d'une documentation soigneuse car ce n'est pas la façon dont les événements fonctionnent normalement.

+0

Pour éviter l'erreur du compilateur, lors du déclenchement de l'événement, utilisez le nom «privé» de l'événement. 'foo' vs 'Foo' – Zyo

4

J'ai tendance à ajouter un gestionnaire d'événements dans un chemin qui est exécuté une seule fois, par exemple dans un constructeur.

2

Vous pouvez implémenter votre propre stockage des delgates et vérifier l'unicité lors de leur ajout à l'événement. Voir la classe EventOwner2 ci-dessous pour un exemple. Je ne sais pas comment cela fonctionne, mais encore une fois, ce n'est pas toujours un problème.

using System; 
using System.Collections.Generic; 

namespace EventExperiment 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IEventOwner e=new EventOwner2(); 
      Subscriber s=new Subscriber(e); 
      e.RaiseSome(); 
      Console.ReadKey(); 
     } 
    } 

    /// <summary> 
    /// A consumer class, subscribing twice to the event in it's constructor. 
    /// </summary> 
    public class Subscriber 
    { 
     public Subscriber(IEventOwner eventOwner) 
     { 
      eventOwner.SomeEvent += eventOwner_SomeEvent; 
      eventOwner.SomeEvent += eventOwner_SomeEvent; 
     } 

     void eventOwner_SomeEvent(object sender, EventArgs e) 
     { 
      Console.WriteLine(DateTimeOffset.Now); 
     } 

    } 

    /// <summary> 
    /// This interface is not essensial to this point. it is just added for conveniance. 
    /// </summary> 
    public interface IEventOwner 
    { 
     event EventHandler<EventArgs> SomeEvent; 
     void RaiseSome(); 
    } 

    /// <summary> 
    /// A traditional event. This is raised for each subscription. 
    /// </summary> 
    public class EventOwner1 : IEventOwner 
    { 
     public event EventHandler<EventArgs> SomeEvent = delegate { }; 
     public void RaiseSome() 
     { 
      SomeEvent(this,new EventArgs()); 
     } 
    } 
    /// <summary> 
    /// A custom event. This is raised only once for each subscriber. 
    /// </summary> 
    public class EventOwner2 : IEventOwner 
    { 
     private readonly List<EventHandler<EventArgs>> handlers=new List<EventHandler<EventArgs>>(); 
     public event EventHandler<EventArgs> SomeEvent 
     { 
      add 
      { 
       lock (handlers) 
        if (handlers!=null&&!handlers.Contains(value)) 
        { 
         handlers.Add(value); 
        } 
      } 
      remove 
      { 
       handlers.Remove(value); 
      } 
     } 
     public void RaiseSome() 
     { 
      EventArgs args=new EventArgs(); 
      lock(handlers) 
      foreach (EventHandler<EventArgs> handler in handlers) 
      { 
       handler(this,args); 
      } 
     } 
    } 
} 
0

Quel est le modificateur d'accès de 'object'?

Si c'est privé, vous n'avez qu'à vous soucier de l'objet conteneur qui définit le gestionnaire d'événements. Si c'est interne, vous n'avez qu'à vous soucier de l'ensemble contenant le réglage du gestionnaire d'événements. Si c'est public, alors c'est grand ouvert.

Si 'object' peut être rendu privé sur la classe conteneur, vous pouvez rendre vos contrôles beaucoup plus efficaces en contrôlant l'affectation des gestionnaires d'événements dans la classe locale. Si 'internal' ou 'public' et unicité sont requis, utilisez une classe wrapper qui masque 'object' et présente à la place une méthode pour assigner un gestionnaire d'événements avec vos contrôles derrière lui pour assurer l'unicité.