"La sottrazione dei delegati ha un risultato imprevedibile" in ReSharper / C #?


124

Quando si utilizzano myDelegate -= eventHandlerproblemi con ReSharper (versione 6):

La sottrazione di delegati ha un risultato imprevedibile

Il razionale dietro questo è spiegato da JetBrains qui . La spiegazione ha senso e, dopo averla letta, dubito di tutti i miei usi -sui delegati.

Come dunque ,

  • posso scrivere un evento non automatico senza rendere ReSharper scontroso?
  • oppure esiste un modo migliore e / o "corretto" per implementarlo?
  • oppure posso semplicemente ignorare ReSharper?

Ecco il codice semplificato:

public delegate void MyHandler (object sender);

MyHandler _myEvent;

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

Mono dà lo stesso avvertimento. Ecco la descrizione di R # del problema confluence.jetbrains.com/display/ReSharper/… (che si applica solo agli elenchi di delegati)
thoredge

Risposte:


134

Non aver paura! La prima parte dell'avviso di ReSharper si applica solo alla rimozione di elenchi di delegati. Nel codice rimuovi sempre un singolo delegato. La seconda parte parla dell'ordinamento dei delegati dopo la rimozione di un delegato duplicato. Un evento non garantisce un ordine di esecuzione per i suoi iscritti, quindi non influisce nemmeno su di te.

Poiché i meccanismi di cui sopra possono portare a risultati imprevedibili, ReSharper emette un avviso ogni volta che incontra un operatore di sottrazione delegato.

ReSharper sta emettendo questo avviso perché la sottrazione del delegato multicast può avere trucchi, non sta condannando completamente quella caratteristica del linguaggio. Fortunatamente questi trucchi sono in casi marginali ed è improbabile che li incontri se stai solo strumentando eventi semplici. Non c'è modo migliore per implementare i tuoi add/ removegestori, devi solo prenderne atto.

Suggerirei di ridurre il livello di avviso di ReSharper per quel messaggio a "Suggerimento" in modo da non essere desensibilizzato ai loro avvisi, che di solito sono utili.


67
Penso che sia un male da parte di R # chiamare i risultati "imprevedibili". Sono specificati molto chiaramente. "Non ciò che l'utente potrebbe prevedere" non è in alcun modo la stessa cosa di "imprevedibile". (E 'anche inesatto dire che le definisce framework .NET sovraccarichi -. È cotto nel compilatore C # Delegatefa non sovraccaricare +e -.)
Jon Skeet

6
@ Jon: sono d'accordo. Immagino che tutti si siano abituati all'alto livello che Microsoft si è prefissato. Il livello di lucidatura è così alto, con così tante cose nel mondo .NET che ti fanno "cadere nella fossa del successo", incontrando una caratteristica del linguaggio che è una semplice camminata veloce accanto alla fossa dove c'è un possibilità che tu possa perdere è considerato da alcuni come stridente e merita un cartello che diceva PIT OF SUCCESS IS THAT WAY --->.
Allon Guralnek

5
@ AllonGuralnek: D'altra parte, quando è stata l'ultima volta che hai sentito di qualcuno che ha effettivamente avuto un problema a causa di questo?
Jon Skeet

5
@ Jon: Hai sentito un problema con esso? Non sapevo nemmeno di questo comportamento prima che questa domanda fosse pubblicata.
Allon Guralnek

2
È curioso che R # avverta della sottrazione di delegati, ma non delle implementazioni comuni di eventi che hanno lo stesso identico problema . Il problema principale è che .net utilizza un singolo Delegate.Combineche "appiattisce" i delegati multicast, quindi se gli vengono dati i delegati [X, Y] e Z, non può dire se il risultato deve essere [X, Y, Z] o [[ X, Y], Z] (quest'ultimo delegato che tiene il [X,Y]delegato come suo Targete il Invokemetodo di quel delegato come suo Method).
supercat

28

Non utilizzare direttamente i delegati per sommare o sottrarre. Invece il tuo campo

MyHandler _myEvent;

Dovrebbe invece essere dichiarato anche come evento. Questo risolverà il problema senza rischiare la tua soluzione e avrai comunque il vantaggio dell'utilizzo degli eventi.

event MyHandler _myEvent;

L'utilizzo della somma o della sottrazione del delegato è pericoloso perché è possibile perdere eventi quando si assegna semplicemente il delegato (come da dichiarazione, lo sviluppatore non dedurrà direttamente che si tratta di un delegato multicast come quando viene dichiarato come evento). Giusto per esemplificare, se la proprietà menzionata in questa domanda non è stata contrassegnata come un evento, il codice sottostante farà sì che i primi due incarichi siano PERSI, perché qualcuno semplicemente assegnato al delegato (che è anche valido!).

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

Durante l'assegnazione di Method3, ho perso completamente i due abbonamenti iniziali. L'utilizzo degli eventi eviterà questo problema e allo stesso tempo rimuoverà l'avviso ReSharper.


Non avrei mai pensato di farlo in questo modo, ma ha senso proteggere l'utilizzo del delegato dell'evento purché si mantenga privato l'evento sottostante. Tuttavia, ciò non funziona bene quando è presente una sincronizzazione di thread più specifica che deve avvenire nei gestori di aggiunta / rimozione, ad esempio più sottoscrizioni di eventi o sottoscrizione di cui è necessario tenere traccia. Tuttavia, in ogni caso rimuove gli avvisi.
Jeremy

-17

impostalo su = null invece di usare - =


5
il removemetodo di un evento non dovrebbe rimuovere tutti i gestori, ma piuttosto il gestore che è stato richiesto di rimuovere.
Servizio

se ne ha aggiunto solo uno, se ne rimuove solo uno in effetti, li ha rimossi tutti. Non sto dicendo di usarlo in tutti i casi solo per questo specifico messaggio di affilatura.
Jonathan Beresford

6
Ma non sai che il delegato rimuove sempre l'unico elemento nell'elenco delle chiamate. Questo sta facendo sparire il messaggio di resharper trasformando il codice funzionante corretto in un codice non funzionante errato che, in determinate circostanze, funzionerà per coincidenza, ma in molti casi si interromperà in modi insoliti e difficili da diagnosticare.
Servizio

Ho dovuto votare a favore di questo perché -17 è una penalità troppo dura per qualcuno che si prende il tempo di scrivere una risposta "potenziale". +1 per non essere passivo, anche se hai sbagliato.
John C
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.