2010-01-16 9 views
10

Ist es möglich, mehrere Abonnenten daran zu hindern, ein Ereignis zu abonnieren?Kann ein Event-Listener auf nur einen Abonnenten beschränkt sein?

Ich habe ein schnelles Beispiel-Snippet erstellt, um meiner Frage einen Kontext zu geben, aber leider kann ich es jetzt nicht testen, weil ich nicht auf meiner VS-Maschine bin.

Ziel ist es:

  • Rückkehr eine leere Liste, wenn es keine Abonnenten sind.
  • Geben Sie den Rückgabewert eines einzelnen Abonnenten zurück.
  • Eine Ausnahme auslösen, wenn mehr als ein Abonnent versucht, das Ereignis zu abonnieren (Dies ist der Kern der Angelegenheit).

Ist das möglich?

public delegate List<IBaseWindow> GetWindowListDelegate(); 
public static event GetWindowListDelegate GetWindowListEvent; 

public List<IBaseWindow> GetWindowList() { 

    if (GetWindowListEvent == null) { 
     return new List<IBaseWindow>(); 
    } 

    return GetWindowListEvent(); 
} 

Hinweis: ich .NET 3.5 SP1 verwenden.

Antwort

11

Es klingt, als ob Sie kein Ereignis benötigen - stellen Sie nur den Delegaten selbst zur Verfügung und erlauben Sie den Anrufern, die Delegiertenreferenz selbst festzulegen.

+0

Tut mir leid, aber ich verstehe nicht, Delegat gut genug zu wissen, was du meinst ... mein Verständnis von Delegierten ist auf ihre Verwendung mit Veranstaltungen beschränkt. Vielleicht hat das begrenzt, wie ich mich diesem Problem annähere. Können Sie mir ein Beispiel geben, während ich Delegierte lese? – InvertedAcceleration

+2

Es gibt nichts schwierig zu verstehen, nur entfernen Sie das 'Ereignis' Schlüsselwort und" subscribe "mit = anstelle von + = –

+8

AFAIK alle Delegaten in .NET sind standardmäßig Multi-Cast-Delegaten, d. H. Sie können mehrere" Abonnenten "haben. – dtb

7

Sie könnten event accessors verwenden, um dies zu erreichen. So etwas wie das Folgende:

private EventHandler _h; 
    public event EventHandler H { 
     add { 
     if (...) { // Your conditions here. 
        // Warning (as per comments): clients may not 
        // expect problems to occur when adding listeners! 
      _h += value; 
     } 
     } 
     remove { 
     _h -= value; 
     } 
    } 

Wie Andrew betont, brauchen Sie keine Ereignisse, um dies zu erreichen. Gibt es einen bestimmten Grund, warum Sie sie brauchen?

+8

+1 als Antwort. Ich würde hinzufügen, vorsichtig zu sein mit dieser Technik - wenn ein .NET-Entwickler einen Typ ein Ereignis deklarieren sieht, ist die * safe * -Annahme (so sicher gibt es nicht einmal einen Grund, etwas anderes zu überprüfen), dass neue Listener ohne Probleme hinzugefügt werden können. –

+0

+1 sowohl für die Antwort als auch für den Kommentar von @ 280Z28. –

+0

Guter Anruf. Ich habe eine Notiz hinzugefügt. –

0

Es ist sicherlich möglich zu erreichen, was Sie tun möchten, aber es entspricht nicht der Konvention - ich würde Sie drängen, mit einer anderen Lösung zu kommen, die keine Ereignisse beinhaltet.

As explained by Jon Skeet, öffentliche Ereignisse sind eigenschaftsähnliche Wrapper um einen Multicast-Delegaten.

Sie können sich die Standardimplementierung eines Ereignisses als eine Liste von Funktionen vorstellen, die aufgerufen werden, wenn etwas passiert.

// From the above link: 

// the exposed event 
public event EventHandler MyEvent 

// multicast delegate field 
private EventHandler _myEvent; 

// property-like add & remove handlers 
public event EventHandler MyEvent 
{ 
    add 
    { 
     lock (this) 
     { 
      _myEvent += value; 
     } 
    } 
    remove 
    { 
     lock (this) 
     { 
      _myEvent -= value; 
     } 
    }   
} 

... wenn ein Entwickler, ein solches Ereignis sieht, sie erwarten, dass es ohne Ausgabe abonnieren zu können, als ein Ereignis im Grunde sagt, dass „eine beliebige Anzahl von Arten registrieren kann dieses Ereignis eine Benachrichtigung zu erhalten“.

Es scheint, dass Sie möchten, dass jemand die Implementierung festlegen kann, die die Liste der Windows erhält. Was ich vorschlagen würde, ist es, den Leuten zu erlauben, einen Delegierten manuell einzutragen, und dann eine einzelne Delegierte Instanz zu halten. Machen Sie deutlich, dass es nur eine Möglichkeit gibt, sie festzulegen. Wenn überhaupt möglich, würde ich die Verwendung von Konstruktorinjektion empfehlen, da dies alle Zweideutigkeiten beseitigt - Sie können die Delegat-Instanz nur einmal bei der Konstruktion setzen, dann ist sie nicht mehr durch die öffentliche API modifizierbar (Klasse B kann also den bereits gesetzten Delegaten nicht überlisten) nach Klasse A).

z.

public class CallsDelegateToDoSomething 
{  
    private Func<List<IBaseWindow>> m_windowLister; 

    public CallsDelegateToDoSomething(Func<List<IBaseWindow>> windowFunc) 
    { 
     m_windowLister = windowFunc; 
    } 

    public List<IBaseWindow> GetWindowList() 
    {  
     if (windowLister == null) 
     { 
      return new List<IBaseWindow>(); 
     } 

     return m_windowLister(); 
    } 
} 

Wenn Ihr Design für diese nicht zulässt, dann erstellen Sie einfach SetWindowLister(Func<List<IBaseWindow>> windowLister) und ClearWindowLister() stattdessen Methoden.

+1

Selbst ein "einzelner Delegierter" kann zum Aufruf mehrerer Ziele führen. – dtb

+0

Vielen Dank für eine solche vollständige Antwort und das letzte Beispiel. Ich betrachte jetzt Delegierte noch einmal, um den Kommentar von dtb zu verstehen. – InvertedAcceleration

+0

dtb: Gibt es keine elegante Möglichkeit, die Aufrufliste abzurufen und sicherzustellen, dass sie den Wert 1 hat? Gibt es eine elegante Möglichkeit, sicherzustellen, dass ein Delegat kein Delegierter mit mehreren Teilnehmern ist? Ich arbeite normalerweise mit Schnittstellen für diese Art von Sache (z. B. IWindowLister), so dass das Problem gelöst wird, aber bedeutet, dass Sie stattdessen eine Schnittstelle erstellen und implementieren müssen, die nicht immer geeignet ist. –

5

Nur John Antwort zu vervollständigen, ist hier eine funktionierende Implementierung eines Ereignisses, das einen Handler nur erlaubt:

class Foo 
{ 
    private EventHandler _bar; 
    public event EventHandler Bar 
    { 
     add 
     { 
      if (_bar != null || value.GetInvocationList().Length > 1) 
      { 
       throw new InvalidOperationException("Only one handler allowed"); 
      } 
      _bar = (EventHandler)Delegate.Combine(_bar, value); 
     } 
     remove 
     { 
      _bar = (EventHandler)Delegate.Remove(_bar, value); 
     } 
    } 
} 

Beachten Sie, dass statt einer Veranstaltung einen Delegaten Aussetzen nicht daran hindert, mehrere Handler: seit .NET Delegierten Multicast sind, kann ein Delegat einen Aufruf an mehrere Methoden darstellen. Sie können den Delegaten jedoch als Eigenschaft verfügbar machen und im Setter den gleichen Check wie im obigen Code durchführen.

Wie auch andere darauf hingewiesen haben, ist es wahrscheinlich keine gute Idee, mehrere Handler für ein Ereignis zu verhindern ... es wäre sehr verwirrend für Entwickler, die es verwenden.

+0

Vielen Dank für die zusätzliche Zeit, um weitere Informationen zur Verfügung zu stellen. Ich bin definitiv der Meinung, dass das ursprüngliche Design falsch war. – InvertedAcceleration

2

Mit diesem Code geben Sie YourNameHere-Delegat frei, aber es deaktiviert die + = -Funktionalität und erlaubt ONLY =.

private Action<byte[]> yourNameHere; 

public Action<byte[]> YourNameHere 
{ 
    set { yourNameHere= value; } 
} 

Ich hoffe, es hilft.

Verwandte Themen