„Odejmowanie delegatów ma nieprzewidywalny wynik” w ReSharper / C #?

124

W przypadku myDelegate -= eventHandlerproblemów z ReSharper (wersja 6):

Odejmowanie delegatów ma nieprzewidywalny wynik

Racjonalne uzasadnienie tego jest wyjaśnione przez JetBrains tutaj . Wyjaśnienie ma sens, a po jego przeczytaniu wątpię w to, co robię -na delegatach.

Jak wtedy ,

  • czy mogę napisać wydarzenie inne niż automatyczne bez zrzędliwości ReSharper?
  • czy też jest lepszy i / lub „poprawny” sposób realizacji tego?
  • czy mogę po prostu zignorować ReSharper?

Oto uproszczony kod:

public delegate void MyHandler (object sender);

MyHandler _myEvent;

public event MyHandler MyEvent
{
    add
    {
        _myEvent += value;
        DoSomethingElse();
    }
    remove
    {
        _myEvent -= value; // <-- ReSharper warning here
    }
}

źródło
Mono daje to samo ostrzeżenie. Oto opis problemu przez R # confluence.jetbrains.com/display/ReSharper/… (który dotyczy tylko list delegatów)
thoredge

Odpowiedzi:

134

Nie bój się! Pierwsza część ostrzeżenia ReSharper dotyczy tylko usuwania list delegatów. W swoim kodzie zawsze usuwasz jednego delegata. W drugiej części omówiono porządkowanie delegatów po usunięciu zduplikowanego delegata. Wydarzenie nie gwarantuje kolejności realizacji swoim subskrybentom, więc tak naprawdę nie wpływa na Ciebie.

Ponieważ powyższa mechanika może prowadzić do nieprzewidywalnych rezultatów, ReSharper generuje ostrzeżenie za każdym razem, gdy napotka delegata operatora odejmowania.

ReSharper wydaje to ostrzeżenie, ponieważ odejmowanie delegatów multiemisji może mieć problemy, nie potępia całkowicie tej funkcji języka. Na szczęście te pułapki występują w marginalnych przypadkach i jest mało prawdopodobne, abyś je spotkał, jeśli tylko instrumentujesz proste zdarzenia. Nie ma lepszego sposobu na implementację własnych add/ removehandlerów, po prostu musisz zwrócić uwagę.

Proponuję obniżyć poziom ostrzeżenia ReSharper dla tej wiadomości do „Podpowiedź”, aby nie znieczulić ich ostrzeżeń, które są zwykle przydatne.

Allon Guralnek
źródło
67
Myślę, że to złe ze strony R # nazywać wyniki „nieprzewidywalnymi”. Są bardzo jasno określone. „Nie to, co użytkownik mógłby przewidzieć” nie jest w żaden sposób tym samym, co „nieprzewidywalne”. (Jest to również niedokładne powiedzieć, że Definiuje .NET Framework przeciążenia. - to pieczone do kompilatora C # Delegateczy nie przeciążać +i -.)
Jon Skeet
6
@Jon: Zgadzam się. Chyba wszyscy przyzwyczaili się do wysokiego poprzeczki, jaką Microsoft postawił sobie. Poziom języka polskiego jest tak wysoki, jak jest, z tak wieloma rzeczami w świecie .NET, które sprawiają, że „wpadasz w otchłań sukcesu”, napotykając cechę języka, która polega na zwykłym szybkim spacerze obok dołu, w którym znajduje się Szansa, którą możesz przegapić, jest przez niektórych uważana za drażniącą i gwarantuje znak z napisem PIT OF SUCCESS IS THAT WAY --->.
Allon Guralnek
5
@AllonGuralnek: Z drugiej strony, kiedy ostatnio słyszałeś, że ktoś ma z tego powodu problem?
Jon Skeet
5
@Jon: Słyszałeś o problemie? I nawet nie wiedzieć o tym, zanim to kwestia zachowania została wysłana.
Allon Guralnek
2
Ciekawe, że R # ostrzegałby o odejmowaniu delegatów, ale nie ostrzegał o typowych implementacjach zdarzeń, które mają dokładnie ten sam problem . Podstawowy problem polega na tym, że .net używa pojedynczego, Delegate.Combinektóry „spłaszcza” delegatów multiemisji, więc jeśli ma przydzielonych delegatów [X, Y] i Z, nie może stwierdzić, czy wynikiem powinno być [X, Y, Z] czy [[ X, Y], Z] (ten ostatni delegat trzyma [X,Y]delegata jako swojego Targeti Invokemetodę tego delegata jako swoją Method).
supercat
28

Nie należy bezpośrednio używać delegatów do sumowania lub odejmowania. Zamiast twojego pola

MyHandler _myEvent;

Zamiast tego powinno być również zadeklarowane jako wydarzenie. To rozwiąże problem bez ryzykowania rozwiązania i nadal będzie korzystać z funkcji zdarzenia.

event MyHandler _myEvent;

Użycie sumy lub odejmowania delegata jest niebezpieczne, ponieważ możesz utracić zdarzenia podczas prostego przypisywania delegata (zgodnie z deklaracją deweloper nie wywnioskuje bezpośrednio, że jest to delegat multiemisji, gdy jest zadeklarowany jako zdarzenie). Dla przykładu, jeśli właściwość wymieniona w tym pytaniu nie została oflagowana jako zdarzenie, poniższy kod spowoduje, że dwa pierwsze przypisania zostaną UTRACONE, ponieważ ktoś po prostu przypisany do delegata (co również jest ważne!).

myObject.MyEvent += Method1; 
myObject.MyEvent += Method2;
myObject.MyEvent = Method3;

Podczas przypisywania Method3 całkowicie straciłem dwie początkowe subskrypcje. Użycie zdarzenia pozwoli uniknąć tego problemu i jednocześnie usunie ostrzeżenie ReSharper.

blimac
źródło
Nigdy nie myślałem o zrobieniu tego w ten sposób, ale ma sens ochrona użycia delegata zdarzenia, o ile zachowuje się to zdarzenie podstawowe jako prywatne. Nie działa to jednak dobrze, gdy istnieje bardziej szczegółowa synchronizacja wątków, która musi wystąpić w programach obsługi dodawania / usuwania, takich jak wiele subskrypcji zdarzeń lub subskrypcji, które również muszą być śledzone. Jednak w każdym przypadku usuwa ostrzeżenia.
Jeremy
-17

ustaw go na = null zamiast używać - =

Jonathan Beresford
źródło
5
removemetoda zdarzenie nie powinno usunąć wszystkie programy obsługi, ale raczej handler że została wezwana do usunięcia.
Servy
jeśli dodał tylko jedną, to jeśli usunie tylko jedną, w efekcie usuwa je wszystkie. Nie mówię, że używaj go we wszystkich przypadkach tylko do tej konkretnej wiadomości.
Jonathan Beresford
6
Ale nie wiesz, że delegat zawsze usuwa jedyny element z listy wywołań. Powoduje to, że komunikat resharpera znika, zamieniając poprawny działający kod w niepoprawny uszkodzony kod, który w pewnych okolicznościach zadziała przypadkowo, ale w wielu przypadkach będzie się łamał w nietypowy i trudny do zdiagnozowania sposób.
Servy
Musiałem to zagłosować, ponieważ -17 jest zbyt surową karą dla kogoś, kto poświęca czas na napisanie „potencjalnej” odpowiedzi. +1 za brak bierności, nawet jeśli się myliłeś.
John C