"Delegatensubtraktion hat unvorhersehbares Ergebnis" in ReSharper / C #?

124

Bei Verwendung von myDelegate -= eventHandlerReSharper (Version 6) treten folgende Probleme auf:

Die Delegatensubtraktion hat ein unvorhersehbares Ergebnis

Das Rationale dahinter wird hier von JetBrains erklärt . Die Erklärung ist sinnvoll und nachdem ich sie gelesen habe, bezweifle ich, dass ich sie -für Delegierte verwende.

Wie dann ,

  • Kann ich ein nicht automatisches Ereignis schreiben, ohne ReSharper mürrisch zu machen?
  • oder gibt es einen besseren und / oder "richtigen" Weg, dies umzusetzen?
  • oder kann ich ReSharper einfach ignorieren?

Hier ist vereinfachter Code:

public delegate void MyHandler (object sender);

MyHandler _myEvent;

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

quelle
Mono gibt die gleiche Warnung. Hier ist R # 's Beschreibung des Problems Confluence.jetbrains.com/display/ReSharper/… (das nur für Delegiertenlisten gilt)
Thoredge

Antworten:

134

Hab keine Angst! Der erste Teil der Warnung von ReSharper gilt nur für das Entfernen von Delegiertenlisten. In Ihrem Code entfernen Sie immer einen einzelnen Delegaten. Der zweite Teil befasst sich mit der Bestellung von Delegierten, nachdem ein doppelter Delegierter entfernt wurde. Ein Ereignis garantiert seinen Abonnenten keine Ausführungsreihenfolge, sodass Sie auch nicht wirklich betroffen sind.

Da die oben genannten Mechanismen zu unvorhersehbaren Ergebnissen führen können, gibt ReSharper eine Warnung aus, wenn ein delegierter Subtraktionsoperator auftritt.

ReSharper gibt diese Warnung aus, da die Subtraktion von Multicast-Delegaten Fallstricke haben kann. Diese Sprachfunktion wird nicht vollständig verurteilt. Glücklicherweise befinden sich diese Fallstricke in Randfällen, und es ist unwahrscheinlich, dass Sie ihnen begegnen, wenn Sie nur einfache Ereignisse instrumentieren. Es gibt keinen besseren Weg, um Ihre eigenen add/ removeHandler zu implementieren. Sie müssen nur darauf achten.

Ich würde vorschlagen, die Warnstufe von ReSharper für diese Nachricht auf "Hinweis" herunterzustufen, damit Sie nicht für die Warnungen desensibilisiert werden, die normalerweise nützlich sind.

Allon Guralnek
quelle
66
Ich denke, es ist schlecht von R #, die Ergebnisse als "unvorhersehbar" zu bezeichnen. Sie sind sehr klar spezifiziert. "Nicht das, was der Benutzer vorhersagen könnte" ist in keiner Weise dasselbe wie "unvorhersehbar". (Es ist auch ungenau zu sagen , dass die .NET - Framework definiert Überlastungen -. Es in die C # -Compiler gebacken ist Delegatenicht nicht überlasten +und -.)
Jon Skeet
6
@ Jon: Ich stimme zu. Ich denke, jeder hat sich an die hohe Messlatte gewöhnt, die Microsoft für sich selbst festgelegt hat. Das Niveau der Politur ist so hoch wie es ist, mit so vielen Dingen in der .NET-Welt, die Sie "in die Grube des Erfolgs fallen lassen" und auf ein Sprachmerkmal stoßen, das nur ein flotter Spaziergang neben der Grube ist, in der es eine gibt Die Möglichkeit, dass Sie es verpassen, wird von einigen als störend empfunden und rechtfertigt ein Schild mit der Aufschrift PIT OF SUCCESS IS THAT WAY --->.
Allon Guralnek
5
@AllonGuralnek: Wann haben Sie das letzte Mal von jemandem gehört, der aufgrund dieses Problems tatsächlich ein Problem hat?
Jon Skeet
5
@ Jon: Hast du von einem Problem damit gehört? Ich wusste nicht einmal über dieses Verhalten Bescheid, bevor diese Frage gestellt wurde.
Allon Guralnek
2
Es ist merkwürdig, dass R # vor der Subtraktion von Delegaten warnt, aber nicht vor den allgemeinen Implementierungen von Ereignissen, die genau das gleiche Problem haben . Das Hauptproblem besteht darin, dass .net eine einzelne verwendet, Delegate.Combinedie Multicast-Delegaten "abflacht". Wenn also die Delegierten [X, Y] und Z angegeben werden, kann nicht angegeben werden, ob das Ergebnis [X, Y, Z] oder [[ X, Y], Z] (der letztere Delegierte hält den [X,Y]Delegierten als seinen Targetund die InvokeMethode dieses Delegierten als seinen Method).
Supercat
28

Sie sollten Delegaten nicht direkt zum Summieren oder Subtrahieren verwenden. Stattdessen dein Feld

MyHandler _myEvent;

Sollte stattdessen auch als Ereignis deklariert werden. Dies löst das Problem, ohne Ihre Lösung zu gefährden, und bietet dennoch den Vorteil der Verwendung von Ereignissen.

event MyHandler _myEvent;

Die Verwendung der Delegatensumme oder -subtraktion ist gefährlich, da Sie beim einfachen Zuweisen des Delegaten Ereignisse verlieren können (gemäß der Deklaration wird der Entwickler nicht direkt darauf schließen, dass es sich um einen Multicast-Delegaten handelt, wie wenn er als Ereignis deklariert wird). Um nur zu veranschaulichen, wenn die in dieser Frage erwähnte Eigenschaft nicht als Ereignis gekennzeichnet wurde, werden im Code unten die beiden ersten Zuweisungen als VERLOREN eingestuft, da jemand einfach dem Delegaten zugewiesen wurde (was ebenfalls gültig ist!).

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

Bei der Zuweisung von Methode 3 habe ich die beiden Erstabonnements vollständig verloren. Durch die Verwendung von Ereignissen wird dieses Problem vermieden und gleichzeitig die ReSharper-Warnung entfernt.

Blimac
quelle
Ich habe nie daran gedacht, dies so zu tun, aber es ist sinnvoll, die Verwendung von Ereignisdelegierten zu schützen, solange Sie das zugrunde liegende Ereignis privat halten. Dies funktioniert jedoch nicht gut, wenn in den Handlern zum Hinzufügen / Entfernen spezifischere Thread-Synchronisierungen erforderlich sind, z. B. mehrere Ereignisabonnements oder Unterabonnements, die ebenfalls verfolgt werden müssen. In jedem Fall werden jedoch die Warnungen entfernt.
Jeremy
-17

Setzen Sie es auf = null, anstatt - = zu verwenden

Jonathan Beresford
quelle
5
Die removeMethode eines Ereignisses sollte nicht alle Handler entfernen, sondern den Handler, dessen Entfernung angefordert wurde.
Servy
Wenn er nur einen hinzugefügt hat, hat er alle entfernt, wenn er nur einen entfernt hat. Ich sage nicht, dass Sie es in allen Fällen nur für diese spezielle Resharper-Nachricht verwenden sollen.
Jonathan Beresford
6
Sie wissen jedoch nicht , dass der Delegat immer das einzige Element in der Aufrufliste entfernt. Dies führt dazu, dass die Resharper-Nachricht verschwindet, indem korrekter Arbeitscode in falschen fehlerhaften Code umgewandelt wird, der unter bestimmten Umständen zufällig funktioniert, in vielen Fällen jedoch auf ungewöhnliche und schwer zu diagnostizierende Weise unterbrochen wird.
Servy
Ich musste dies positiv bewerten, weil -17 eine zu harte Strafe für jemanden ist, der sich die Zeit nimmt, eine "potenzielle" Antwort zu schreiben. +1 für nicht passiv zu sein, auch wenn Sie falsch waren.
John C