Ich habe eine generische Methode wie folgt definiert:
public void MyMethod<T>(T myArgument)
Als erstes möchte ich überprüfen, ob der Wert von myArgument der Standardwert für diesen Typ ist.
if (myArgument == default(T))
Dies wird jedoch nicht kompiliert, da ich nicht garantiert habe, dass T den Operator == implementiert. Also habe ich den Code auf Folgendes umgestellt:
if (myArgument.Equals(default(T)))
Jetzt wird dies kompiliert, schlägt jedoch fehl, wenn myArgument null ist. Dies ist Teil dessen, worauf ich teste. Ich kann eine explizite Nullprüfung wie folgt hinzufügen:
if (myArgument == null || myArgument.Equals(default(T)))
Das fühlt sich für mich überflüssig an. ReSharper schlägt sogar vor, den Teil myArgument == null in myArgument == default (T) zu ändern, mit dem ich begonnen habe. Gibt es einen besseren Weg, um dieses Problem zu lösen?
Ich muss sowohl Referenztypen als auch Werttypen unterstützen.
if (myArgument?.Equals( default(T) ) != null )
.true
jeden Fall ausgewertet, daEquals
immer für Werttypen aufgerufen wird, damyArgument
diesnull
in diesem Fall nicht möglich ist und das Ergebnis vonEquals
(einem Booleschen ) niemals sein wirdnull
.Antworten:
Um Boxen zu vermeiden, ist der beste Weg, Generika auf Gleichheit zu vergleichen, mit
EqualityComparer<T>.Default
. Dies respektiertIEquatable<T>
(ohne Boxen) sowieobject.Equals
alleNullable<T>
"angehobenen" Nuancen und behandelt sie . Daher:Dies wird übereinstimmen:
Nullable<T>
quelle
Person
,p1.Equals(p2)
würde davon abhängen , ob es GeräteIEquatable<Person>
auf der öffentlichen API oder über explizite Implementierung - also eine öffentliche die Compiler sehenEquals(Person other)
Methode. Jedoch; Bei Generika wird für alle dieselbe IL verwendetT
. ein ,T1
die zur Umsetzung geschiehtIEquatable<T1>
Bedürfnisse behandelt wird identisch zu einemT2
, der nicht - also nein, es wird nicht ein FleckEquals(T1 other)
Verfahren, auch wenn es zur Laufzeit vorhanden ist . In beiden Fällen gibt es auchnull
zu überlegen (jedes Objekt). Bei Generika würde ich also den Code verwenden, den ich gepostet habe.Wie wäre es damit:
Durch die Verwendung der
static object.Equals()
Methode müssen Sie dienull
Überprüfung nicht selbst durchführen.object.
Abhängig von Ihrem Kontext ist es wahrscheinlich nicht erforderlich, den Anruf explizit zu qualifizieren , aber normalerweisestatic
stelle ich Anrufen den Typnamen voran, um den Code löslicher zu machen.quelle
Ich konnte einen Microsoft Connect-Artikel finden , in dem dieses Problem ausführlich behandelt wird:
Folgendes können Sie tun ...
Ich habe bestätigt, dass beide Methoden für einen generischen Vergleich von Referenz- und Werttypen funktionieren:
oder
Um Vergleiche mit dem Operator "==" durchzuführen, müssen Sie eine der folgenden Methoden verwenden:
Wenn alle Fälle von T von einer bekannten Basisklasse stammen, können Sie den Compiler mithilfe generischer Typbeschränkungen informieren.
Der Compiler erkennt dann, wie Operationen ausgeführt werden
MyBase
und gibt nicht aus, dass der Operator "==" nicht auf Operanden vom Typ "T" und "T" angewendet werden kann, die jetzt angezeigt werden.Eine andere Möglichkeit wäre, T auf jeden Typ zu beschränken, der implementiert
IComparable
.Verwenden Sie dann die
CompareTo
von der IComparable-Schnittstelle definierte Methode .quelle
Versuche dies:
das sollte kompilieren und tun, was Sie wollen.
quelle
Equals
Methode verwendetIEqualityComparer
zwei Argumente, die beiden zu vergleichenden Objekte. Nein, sie ist nicht redundant.(Bearbeitet)
Marc Gravell hat die beste Antwort, aber ich wollte ein einfaches Code-Snippet veröffentlichen, das ich ausgearbeitet habe, um es zu demonstrieren. Führen Sie dies einfach in einer einfachen C # -Konsolen-App aus:
Noch etwas: Kann jemand mit VS2008 dies als Erweiterungsmethode versuchen? Ich stecke hier mit 2005 fest und bin gespannt, ob das erlaubt ist.
Bearbeiten: Hier erfahren Sie, wie Sie es als Erweiterungsmethode verwenden können:
quelle
Um alle Arten von T zu behandeln, einschließlich der Tatsache, dass T ein primitiver Typ ist, müssen Sie beide Vergleichsmethoden kompilieren:
quelle
Hier wird es ein Problem geben -
Wenn Sie zulassen, dass dies für jeden Typ funktioniert, ist Standard (T) für Referenztypen immer null und für Werttypen 0 (oder eine Struktur voller 0).
Dies ist jedoch wahrscheinlich nicht das Verhalten, nach dem Sie suchen. Wenn dies generisch funktionieren soll, müssen Sie wahrscheinlich Reflection verwenden, um den Typ von T zu überprüfen und andere Werttypen als Referenztypen zu behandeln.
Alternativ können Sie eine Schnittstellenbeschränkung festlegen, und die Schnittstelle bietet eine Möglichkeit, den Standard der Klasse / Struktur zu überprüfen.
quelle
Ich denke, Sie müssen diese Logik wahrscheinlich in zwei Teile aufteilen und zuerst nach Null suchen.
Bei der IsNull-Methode verlassen wir uns auf die Tatsache, dass ValueType-Objekte per Definition nicht null sein können. Wenn value also eine Klasse ist, die von ValueType abgeleitet ist, wissen wir bereits, dass sie nicht null ist. Wenn es sich jedoch nicht um einen Wertetyp handelt, können wir den Wert, der mit einem Objekt umgewandelt wurde, einfach mit Null vergleichen. Wir könnten die Prüfung gegen ValueType vermeiden, indem wir direkt zu einem Cast-to-Objekt wechseln. Dies würde jedoch bedeuten, dass ein Werttyp eingerahmt wird, was wir wahrscheinlich vermeiden möchten, da dies impliziert, dass ein neues Objekt auf dem Heap erstellt wird.
In der IsNullOrEmpty-Methode suchen wir nach dem Sonderfall einer Zeichenfolge. Für alle anderen Typen vergleichen wir den Wert (der bereits weiß, dass dies nicht der Fall ist) null ist) mit seinem Standardwert, der für alle Referenztypen null ist und für Werttypen normalerweise eine Form von Null ist (wenn sie ganzzahlig sind).
Mit diesen Methoden verhält sich der folgende Code wie erwartet:
quelle
Verlängerungsmethode basierend auf akzeptierter Antwort.
Verwendungszweck:
Wechseln Sie zur Vereinfachung mit null ab:
Verwendungszweck:
quelle
Ich benutze:
quelle
Sie wissen nicht, ob dies mit Ihren Anforderungen funktioniert oder nicht, aber Sie können T auf einen Typ beschränken, der eine Schnittstelle wie IComparable implementiert, und dann die ComparesTo () -Methode von dieser Schnittstelle (die von IIRC Nullen unterstützt / behandelt) verwenden ::
Es gibt wahrscheinlich andere Schnittstellen, die Sie auch IEquitable usw. verwenden könnten.
quelle
@ilitirit:
Der Operator '==' kann nicht auf Operanden vom Typ 'T' und 'T' angewendet werden.
Ich kann mir keine Möglichkeit vorstellen, dies ohne den expliziten Nulltest zu tun, gefolgt vom Aufrufen der Equals-Methode oder des Equals-Objekts. Equals wie oben vorgeschlagen.
Sie können mit System.Comparison eine Lösung entwickeln, aber das wird tatsächlich zu viel mehr Codezeilen führen und die Komplexität erheblich erhöhen.
quelle
Ich denke du warst nah dran.
Jetzt kompiliert dies, wird aber fehlschlagen, wenn
myArgument
es null ist. ist ein Teil dessen, worauf ich teste. Ich kann eine explizite Nullprüfung wie folgt hinzufügen:Sie müssen nur das Objekt umkehren, für das die Gleichheit aufgerufen wird, um einen eleganten, nullsicheren Ansatz zu erhalten.
quelle