2008-09-30 8 views
125

Nehmen Sie die # -Klasse folgende C:Wie kann ich Ereignisabonnements in C# löschen?

c1 { 
event EventHandler someEvent; 
} 

Wenn es eine Menge von Abonnements für c1 ‚s someEvent Ereignis ist, und ich will sie alle löschen, was ist der beste Weg, dies zu erreichen? Beachten Sie auch, dass Abonnements für dieses Ereignis lambdas/anonyme Delegaten sein können.

Derzeit ist meine Lösung, eine ResetSubscriptions() Methode zu c1 hinzuzufügen, die someEvent auf null setzt. Ich weiß nicht, ob das irgendwelche unsichtbaren Konsequenzen hat.

Antwort

164

Innerhalb der Klasse können Sie die (verborgene) Variable auf null setzen. Eine Nullreferenz ist die kanonische Art, eine leere Aufrufliste effektiv darzustellen.

Von außerhalb der Klasse, können Sie dies nicht tun - Ereignisse im Grunde setzen "abonnieren" und "abbestellen" und das ist es.

Es ist wichtig zu wissen, was Feld-ähnliche Ereignisse tatsächlich tun - sie erstellen eine Variable und ein Ereignis zur gleichen Zeit. Innerhalb der Klasse referenzieren Sie die Variable. Von außen verweisen Sie auf das Ereignis.

Siehe meine article on events and delegates für weitere Informationen.

+9

Oh die Wunder der objektorientierten Programmierung. –

+3

Wenn Sie stur sind, können Sie es durch Reflektion erzwingen. Siehe http://stackoverflow.com/questions/91778/how-to-remove-all-event-handlers-from-a-control/91853#91853. – Brian

+1

@Brian: Es hängt von der Implementierung ab. Wenn es * nur * ein feldähnliches Ereignis oder eine 'EventHandlerList' ist, können Sie dies möglicherweise tun. Sie müssten diese beiden Fälle jedoch erkennen - und es könnte eine beliebige Anzahl anderer Implementierungen geben. –

28

eine Methode c1 hinzufügen, die ‚someevent‘ zu null ...

class c1 
{ 
    event EventHandler someEvent; 
    ResetSubscriptions() {someEvent = null;} 
} 
+0

Sind Sie sicher, dass das Zuweisen von null die Aufrufliste löscht? – leppie

+0

Das ist das Verhalten, das ich sehe. Wie ich in meiner Frage gesagt habe, weiß ich nicht, ob ich etwas übersehen habe. – programmer

5

Sie können dies erreichen, indem Sie die Delegate.Remove oder Delegate.RemoveAll Methoden festgelegt werden.

+6

Ich glaube nicht, dass dies mit Lambda-Ausdrücken oder anonymen Delegaten funktionieren wird. – programmer

+3

Das wäre ein großartiger Vorschlag, aber Sie haben keine Beispiele ... –

5

Das Festlegen des Ereignisses auf null innerhalb der Klasse funktioniert. Wenn Sie eine Klasse entsorgen, sollten Sie das Ereignis immer auf null setzen, der GC hat Probleme mit Ereignissen und kann die verteilte Klasse möglicherweise nicht bereinigen, wenn sie nicht über Ereignisse verfügt.

3

Konzeptionelle erweiterte langweilig Kommentar.

Ich verwende lieber das Wort "Event-Handler" statt "Event" oder "Delegate". Und benutzte das Wort "Event" für andere Sachen. In einigen Programmiersprachen (VB.NET, Object Pascal, Objective-C) wird "Ereignis" als "Nachricht" oder "Signal" bezeichnet und hat sogar ein "Nachrichten" -Schlüsselwort und eine spezifische Zuckersyntax.

const 
    WM_Paint = 998; // <-- "question" can be done by several talkers 
    WM_Clear = 546; 

type 
    MyWindowClass = class(Window) 
    procedure NotEventHandlerMethod_1; 
    procedure NotEventHandlerMethod_17; 

    procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener 
    procedure DoClearEventHandler; message WM_Clear; 
    end; 

Und um auf diese „Botschaft“, ein „Event-Handler“ zu reagieren, zu antworten, ob ein einzelner Delegierter oder mehrere Delegierte ist.

Zusammenfassung: "Event" ist die "Frage", "Event-Handler (s)" sind die Antwort (en).

6

Die beste Vorgehensweise zum Löschen aller Subskribenten besteht darin, das someEvent-Objekt auf null zu setzen, indem Sie eine weitere öffentliche Methode hinzufügen, wenn Sie diese Funktionalität für externe Benutzer freigeben möchten. Dies hat keine unsichtbaren Folgen. Voraussetzung ist, dass Sie SomeEvent mit dem Schlüsselwort 'event' deklarieren.

Bitte sehen Sie das Buch - C# 4.0 in der Nussschale, Seite 125.

Einige hier vorgeschlagenen Delegate.RemoveAll Methode zu verwenden. Wenn Sie es verwenden, könnte der Beispielcode dem folgenden Formular folgen. Aber es ist wirklich dumm.Warum nicht einfach SomeEvent=null innerhalb der ClearSubscribers() Funktion?

public void ClearSubscribers() 
    { 
      SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);// Then you will find SomeEvent is set to null. 
    } 
0

Entfernen Sie alle Ereignisse, die Veranstaltung übernehmen ist eine "Aktion" Typ:

Delegate[] dary = TermCheckScore.GetInvocationList(); 

if (dary != null) 
{ 
    foreach (Delegate del in dary) 
    { 
     TermCheckScore -= (Action) del; 
    } 
} 
+0

Wenn Sie innerhalb des Typs sind, der das Ereignis deklariert hat, müssen Sie dies nicht tun. Sie können es einfach auf null setzen. Wenn Sie sich außerhalb des Typs befinden, können Sie die Aufrufliste des Delegaten nicht abrufen . Außerdem gibt Ihr Code beim Aufruf von 'GetInvocationList' eine Ausnahme aus, wenn das Ereignis null ist. – Servy

6
class c1 
{ 
    event EventHandler someEvent; 
    ResetSubscriptions() {someEvent = delegate{};} 
} 

Es ist besser, delegieren verwenden {} als null

1

Diese meine Lösung ist:

public class Foo : IDisposable 
{ 
    private event EventHandler _statusChanged; 
    public event EventHandler StatusChanged 
    { 
     add 
     { 
      _statusChanged += value; 
     } 
     remove 
     { 
      _statusChanged -= value; 
     } 
    } 

    public void Dispose() 
    { 
     _statusChanged = null; 
    } 
} 

Sie müssenanrufenoder verwenden Sie using(new Foo()){/*...*/} Muster, um alle Mitglieder der Aufrufliste abzubestellen.