2008-09-30 17 views
125

Take następujące klasy C#:Jak mogę wyczyścić subskrypcje zdarzeń w języku C#?

c1 { 
event EventHandler someEvent; 
} 

Jeśli istnieje wiele prenumeraty someEvent imprezy c1 „s i chcę je wszystkie usunąć, co jest najlepszym sposobem osiągnięcia tego celu? Należy również wziąć pod uwagę, że subskrypcje tego wydarzenia mogą być/są delegatami lambdas/anonymous.

Obecnie moje rozwiązanie polega na dodaniu metody ResetSubscriptions() do c1, która ustawia someEvent na wartość null. Nie wiem, czy ma to jakieś niewidoczne konsekwencje.

Odpowiedz

164

Z poziomu klasy można ustawić zmienną (ukrytą) na wartość null. Wartość pusta jest kanonicznym sposobem efektywnego reprezentowania pustej listy wywołań.

Spoza klasy, nie możesz tego zrobić - wydarzenia w zasadzie ujawniają "subskrybuj" i "wypisz się" i to wszystko.

Warto być świadomym tego, co właściwie robią wydarzenia w terenie - tworzą one jednocześnie wydarzenie o zmiennej nazwie. W klasie kończy się odwoływanie do zmiennej. Z zewnątrz odwołujesz się do wydarzenia.

Aby uzyskać więcej informacji, zobacz mój numer article on events and delegates.

+9

O cudach programowania obiektowego. –

+3

Jeśli jesteś uparty, możesz go wymusić poprzez odbicie. Zobacz http://stackoverflow.com/questions/91778/how-to-remove-all-event-handlers-from-a-control/91853#91853. – Brian

+1

@Brian: To zależy od implementacji. Jeśli jest to * tylko * zdarzenie podobne do pola lub 'EventHandlerList', możesz być w stanie. Musisz jednak rozpoznać te dwie sprawy - i może istnieć dowolna liczba innych implementacji. –

28

Dodaj metodę c1 że będą Ustaw 'someEvent' null ...

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

Czy na pewno przypisanie wartości null wyczyści listę wywołań? – leppie

+0

To jest zachowanie, które widzę. Jak powiedziałem w moim pytaniu, nie wiem, czy coś przeoczyłem. – programmer

5

Można to osiągnąć za pomocą metod Delegate.Remove lub Delegate.RemoveAll.

+6

Nie wierzę, że to zadziała z wyrażeń lambda lub anonimowych delegatów. – programmer

+3

To byłaby świetna propozycja, ale nie masz przykładów ... –

5

Ustawienie zdarzenia na wartość null w klasie działa. Kiedy pozbędziesz się klasy, zawsze powinieneś ustawić wydarzenie na wartość null, GC ma problemy z wydarzeniami i może nie wyczyścić unieszkodliwionej klasy, jeśli ma zwisające zdarzenia.

3

Konceptualny rozszerzony nudny komentarz.

Zamiast słowa "event" lub "delegate" używam raczej słowa "event eventler". I użył słowa "wydarzenie" dla innych rzeczy. W niektórych językach programowania (VB.NET, Object Pascal, Objective-C) "zdarzenie" nazywane jest "komunikatem" lub "sygnałem", a nawet słowo kluczowe "wiadomość" i określona składnia cukru.

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; 

I, w celu dostosowania się do tego „wiadomości”, a „obsługi zdarzenia” odpowiedzieć, czy jest jeden delegat lub wielu osób.

Podsumowanie: "Zdarzenie" to "pytanie", "obsługa zdarzeń" to odpowiedź (odpowiedzi).

6

Najlepszą praktyką usuwania wszystkich subskrybentów jest ustawienie wartości someEvent na wartość null poprzez dodanie innej publicznej metody, jeśli chcesz udostępnić tę funkcję na zewnątrz. Nie ma to żadnych niewidocznych konsekwencji. Warunkiem wstępnym jest zapamiętanie SomeEvent ze słowem kluczowym "event".

proszę zobaczyć książkę - C# 4.0 w skrócie, strona 125.

Ktoś tu proponuje się stosowanie Delegate.RemoveAll metody. Jeśli go użyjesz, przykładowy kod może być zgodny z poniższym formularzem. Ale to naprawdę głupie.Dlaczego nie tylko SomeEvent=null w funkcji ClearSubscribers()?

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

Usuń wszystkie zdarzenia, załóżmy zdarzenie jest "Action" typ:

Delegate[] dary = TermCheckScore.GetInvocationList(); 

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

Jeśli jesteś wewnątrz typu, który zadeklarował wydarzenie, nie musisz tego robić, możesz po prostu ustawić go na wartość null, jeśli jesteś poza typem, to nie możesz uzyskać listy inwokacji delegata . Ponadto twój kod zgłasza wyjątek, jeśli zdarzenie ma wartość zerową, wywołując 'GetInvocationList'. – Servy

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

Lepiej jest używać delegata {} niż zerowej

1

To jest moje rozwiązanie:

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

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

Należy zadzwonić pod numer Dispose() lub użyj wzoru using(new Foo()){/*...*/}, aby anulować subskrypcję wszystkich członków listy wywołań.