2008-12-30 9 views
44

Wenn ich einen Ereignishandler zur Laufzeit am zuweisen und es ist in einem Ort, der mehrmals aufgerufen werden kann, was ist die empfohlene Praxis mehr Zuweisungen des gleichen Handler auf das gleiche Ereignis zu verhindern.Verhindern gleiche Ereignishandler Zuordnung mehrmals

object.Event += MyFunction 

Wenn Sie dies an einer Stelle einfügen, die mehr als einmal aufgerufen wird, wird der Handler n-mal ausgeführt (natürlich).

Ich habe Zuflucht alle vorherigen Handler zu entfernen, bevor sie über

object.Event -= MyFunction; 

object.Event += MyFunction; 

Diese hinzuzufügen versucht funktioniert, aber scheint weg irgendwie. Irgendwelche Vorschläge zur richtigen Handhabung;) dieses Szenarios.

+1

Ihre Lösung das Ereignis zunächst entfernt und dann das Hinzufügen ist eigentlich eine hoch bewertete Antwort auf http://stackoverflow.com/questions/937181/c-sharp-pattern-to-prevent-an-event-handler-hooked- zweimal (diese Frage hat auch mehr Antworten zu diesem Thema) – OneWorld

+0

möglich duplicate von [Wie man sicherstellt, dass ein Ereignis nur einmal abonniert wird] (http://stackoverflow.com/questions/367523/how-to-ensure-an-event -is-only-subscribe-to-once) –

Antwort

36

Baget ist direkt über ein explizit umgesetzt Ereignis mit (obwohl es eine Mischung gibt die explizite Schnittstellenimplementierung ist und die vollen Ereignissyntax).

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

Das haben einige seltsame Rand Fällen, wenn Sie jemals Multicastdelegaten hinzuzufügen oder zu entfernen, aber das ist unwahrscheinlich: Sie können sich wahrscheinlich damit durchkommen. Es erfordert auch eine sorgfältige Dokumentation, da Ereignisse normalerweise nicht funktionieren.

+0

Um einen Compilerfehler zu vermeiden, verwenden Sie beim Auslösen des Ereignisses den 'privaten' Namen des Ereignisses. 'foo' vs 'Foo' – Zyo

4

Ich neige dazu, einen Event-Handler in einem Pfad hinzuzufügen, die einmal ausgeführt sind, zum Beispiel in einem Konstruktor.

2

Sie können Ihre eigene Lagerung des delgates, und überprüfen Sie die Einzigartigkeit implementieren, wenn sie auf das Ereignis hinzufügen. Ein Beispiel finden Sie in der Klasse EventOwner2. Ich weiß nicht, wie dies leistungsmäßig funktioniert, aber das ist nicht immer ein Problem.

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

Was ist der Zugriffsmodifikator von 'Objekt'?

Wenn es privat ist, müssen Sie sich nur um das enthaltene Objekt kümmern, das den Ereignishandler setzt. Wenn es intern ist, müssen Sie sich nur um die enthaltene Assembly kümmern, die den Ereignishandler einstellt. Wenn es öffentlich ist, dann ist es weit offen.

Wenn ‚Objekt‘ auf der enthaltenden Klasse privat gemacht werden, können Sie Ihre Schecks viel effizienter machen durch Event-Handler-Zuordnung in der lokalen Klasse zu steuern.

Wenn 'internal' oder 'public' und Eindeutigkeit erforderlich ist, verwenden Sie eine Wrapper-Klasse, die 'object' versteckt und stattdessen eine Methode zum Zuordnen eines Event-Handlers mit Ihren hinterlegten Prüfungen bereitstellt, um die Eindeutigkeit sicherzustellen.

Verwandte Themen