2014-08-29 24 views
10

Ten artykuł stanowi You Can’t Unsubscribe from an Event Using a Lambda Expression.Dlaczego nie mogę zrezygnować z subskrypcji przy użyciu wyrażenia Lambda?

E.g. możesz zapisać w następujący sposób:

d.Barked += (s, e) => Console.WriteLine("Bark: {0}", e); 

ale nie można wypisać się tak:

d.Barked -= (s, e) => Console.WriteLine("Bark: {0}", e); 

Dlaczego? Jaka jest różnica między tym a anulowaniem subskrypcji od delegata, np.

EventHandler<string> handler = (s, e) => Console.WriteLine("Bark: {0}", e); 
d.Barked += handler; 

// ... 

d.Barked -= handler; 
+0

http://stackoverflow.com/questions/183367/unsubscribe-anonymous-method-in-c- sharp –

+0

Tak, to przedstawia to samo, co w artykule, ale nie wyjaśnia, dlaczego. – DaveDev

+0

@TimSchmelter niestety moja przeglądarka może zobaczyć tylko w przeszłości. – DaveDev

Odpowiedz

32

Wszystko sprowadza się do: kiedy dwaj delegaci są traktowani tak samo dla celów delegowania dodawania/odejmowania. Kiedy zrezygnujesz z subskrypcji, zasadniczo używa logiki od Delegate.Remove, która uznaje dwóch delegatów za równorzędną, jeśli zarówno dopasowanie .Target, jak i .Method jest zgodne (przynajmniej dla prostego przypadku delegata z pojedynczą metodą docelową, a multicast jest bardziej skomplikowany do opisania) . A więc: co to jest .Method i .Target na lambda (zakładając, że kompilujemy go do delegata , a nie do wyrażenia)?

Kompilator faktycznie ma dużo swobody tutaj, ale co dzieje jest:

  • jeśli lambda obejmuje zamknięcie nad parametru lub zmiennej, kompilator tworzy metodę (metodę ) w klasie generowanej przez kompilator, która reprezentuje kontekst przechwytywania (który może również zawierać token); docelowy kontekst przechwytywania (który będzie zdefiniowany przez zakres przechwytywania), jeśli lambda nie zawiera zamknięcia nad parametrem lub zmienną, ale wykorzystuje stan jednej instancji poprzez this (niejawny lub jawny) kompilator tworzy metodę instancji (metoda ) na bieżącym typie; celjest bieżąca instancja (this)
  • inaczej kompilator tworzy metody statyczne (metoda ), a celjest null (nawiasem mówiąc, w tym scenariuszu to obejmuje również ładne pole do cache pojedynczy statyczna instancja delegata - tak w tym scenariuszu tylko jeden delegat kiedykolwiek stworzony za lambda)

Co to nie zrobić, jest jednak porównać wiele lambdas z podobnymi organami pragnących zmniejszyć wszelkie . Więc co mam kiedy skompilować kod jest dwa statyczne metody:

[CompilerGenerated] 
private static void <Main>b__0(object s, string e) 
{ 
    Console.WriteLine("Bark: {0}", e); 
} 

[CompilerGenerated] 
private static void <Main>b__2(object s, string e) 
{ 
    Console.WriteLine("Bark: {0}", e); 
} 

(Main jest tu tylko dlatego, że w moim stanowisku badawczym te lambdas są wewnątrz metody Main - ale ostatecznie kompilator może wybrać dowolny niewymawialne nazwy, które wybiera tutaj)

Pierwsza metoda jest używana przez pierwszą wartość lambda; druga metoda jest używana przez drugą lambdę.Tak więc ostatecznie nie działa, ponieważ .Method nie pasuje.

W regularnych C# warunkach, byłoby jak robi:

obj.SomeEvent += MethodOne; 
obj.SomeEvent -= MethodTwo; 

gdzie MethodOne i MethodTwo mają ten sam kod wewnątrz nich; nic nie anuluje.

Może być piękny jeśli kompilator zauważony, ale nie jest to wymagane do , i jako taka jest bezpieczniejsza, że ​​nie wybranych do - może to oznaczać, że różne kompilatory rozpocząć produkcję bardzo różne wyniki.

Na marginesie; może to być bardzo mylące, jeśli to zrobi próbować usunąć duplikat, ponieważ masz również problem z kontekstami przechwytywania - wtedy byłoby tak, że "działało" w niektórych przypadkach, a nie innych - bez bycia oczywistym, które - prawdopodobnie najgorszy możliwy scenariusz.

+0

To smutna ironia, myślę, że nadal jestem tutaj jedyną uprowotą. (To prawie równie ironiczne, że - w moim umyśle - to również bardziej odpowiada "jak", dzięki za naprawienie mojego mentalnego modelu usuwania zdarzeń.) – sehe

+0

Doskonałe wyjaśnienie! Minęło trochę czasu odkąd znalazłem lukę w moim zrozumieniu C#, ale dzisiaj mam nowy wgląd. Musiałem to zrozumieć, ponieważ chciałem użyć wartości referencyjnej metody anonimowej (Typ akcji), aby określić, czy została już zarejestrowana jako wywołanie zwrotne. Teraz wiem, że działałoby to tylko wtedy, gdyby nie było zamknięcia i prawdopodobnie spowodowałoby nieoczekiwane problemy. –

Powiązane problemy