Achten Sie auch auf stackoverflow.com/questions/1972262/… - Wenn Sie nicht vorsichtig sind, wird der Vergleich Ihrer Struktur (eines Werttyps ) mit null gut kompiliert, aber nicht das tun, was Sie erwarten.
Ich frage mich, ob es für die Leistung nicht besser wäre, sie zu verwenden, Complex other = obj as Complexund dann zu prüfen, ob other == nullanstelle einer Verwendung isund dann eine Besetzung ...
Clément
5
@Clement: Das können Sie für eine Struktur nicht tun. Das Ergebnis kann nicht null sein. Sie würden einen Kompilierungsfehler erhalten.
Matthew Watson
@MatthewWatson: Ich denke, man könnte verwenden Complex? other = obj as Complex?, aber nullbare Typen sind oft nicht effizient.
Supercat
1
@HaraldCoppoolse - Werttypen sind natürlich versiegelt, sodass es nicht möglich ist, einen abzuleiten, MyComplexwie Sie vorschlagen.
M.Babcock
1
Warum nicht obj ist SaveOptions op && this == op; ?
Billy Jake O'Connor
45
Sie sollten auch IEquatable <T> implementieren. Hier ist ein Auszug aus den Framework Design Guidelines:
Implementieren Sie IEquatable für Werttypen. Die Object.Equals-Methode für Werttypen verursacht Boxing, und ihre Standardimplementierung ist nicht sehr effizient, da sie die Refektion verwendet. IEquatable.Equals bieten eine viel bessere Leistung und können so implementiert werden, dass kein Boxen verursacht wird.
Befolgen Sie bei der Implementierung von IEquatable.Equals die gleichen Richtlinien wie beim Überschreiben von Object.Equals. In Abschnitt 8.7.1 finden Sie detaillierte Richtlinien zum Überschreiben von Object.Equals
Dies wird also nur für Werttypen verwendet? (keine Referenz?)
UpTheCreek
1
Da Referenztypen bei der Übergabe als Objekt nicht eingerahmt werden müssen, würde IEquatable <T> keinen Vorteil bieten. Werttypen werden normalerweise vollständig auf den Stapel (oder in das Layout der äußeren Typen) kopiert. Um also einen Objektverweis darauf zu erhalten und die Lebensdauer des Objekts korrekt zu handhaben, muss es verpackt (mit einem speziellen Typ umwickelt) und kopiert werden zum Haufen; Nur dann kann der Verweis auf das Heap-Objekt an eine Funktion wie Object.Equals übergeben werden.
Gimpf
15
Leider habe ich nicht genug Ruf, um andere Einträge zu kommentieren. Daher veröffentliche ich hier eine mögliche Verbesserung der Top-Lösung.
Korrigieren Sie mich, wenn ich falsch liege, aber die oben erwähnte Implementierung
Hashcode-Kollisionen sind nicht unbedingt ein Problem. Tatsächlich haben Sie immer die Möglichkeit einer Kollision (Informationen zu Pigionlöchern / Geburtstagsparadoxon). In Ihrem Fall kollidieren Komplex (1,4) und Komplex (4,1) (zugegebenermaßen gab es weniger Kollisionen). Dies hängt von Ihren Daten ab . Der Hashcode wird verwendet, um 99,999% der unerwünschten Objekte (z. B. in einem Wörterbuch) schnell auszusortieren. Die Gleichheitsoperatoren haben das letzte Wort.
DarcyThomas
Das heißt, je mehr Eigenschaften Sie auf der Struktur haben, desto größer ist die Wahrscheinlichkeit einer Kollision. Dies kann ein besserer Hash-Algorithmus sein: stackoverflow.com/a/263416/309634
DarcyThomas
9
Meistens können Sie die Implementierung von Equals und GetHashcode in Strukturen vermeiden, da der Compiler eine automatische Implementierung für Werttypen mit bitweisem Inhalt + Reflexion für Referenzelemente durchführt.
Zur Vereinfachung der Verwendung können Sie also weiterhin == und! = Implementieren.
Meistens können Sie jedoch die Implementierung von Equals und GetHashcode vermeiden.
Ein Fall, in dem Sie Equals und GetHashCode implementieren müssten, betrifft ein Feld, das Sie nicht berücksichtigen möchten.
Zum Beispiel ein Feld, das sich im Laufe der Zeit ändert, wie das Alter einer Person oder die InstantSpeed eines Autos (die Identität des Objekts sollte sich nicht ändern, wenn Sie es wieder im Wörterbuch an derselben Stelle finden möchten).
Die Reflexion ist im Vergleich zu einer manuellen Implementierung viel langsamer. Wenn Sie Wert auf Leistung legen, schreiben Sie diese manuell.
Károly Ozsvárt
3
Der grundlegende Unterschied zwischen den beiden besteht darin, dass der ==Operator statisch ist, dh die geeignete aufzurufende Methode wird zur Kompilierungszeit bestimmt, während die EqualsMethode auf einer Instanz dinamisch aufgerufen wird.
Beides zu definieren ist wahrscheinlich das Beste, auch wenn dies bei Strukturen weniger wichtig ist, da Strukturen nicht erweitert werden können (eine Struktur kann nicht von einer anderen erben).
Antworten:
Ein Beispiel aus msdn
public struct Complex { double re, im; public override bool Equals(Object obj) { return obj is Complex c && this == c; } public override int GetHashCode() { return re.GetHashCode() ^ im.GetHashCode(); } public static bool operator ==(Complex x, Complex y) { return x.re == y.re && x.im == y.im; } public static bool operator !=(Complex x, Complex y) { return !(x == y); } }
quelle
Complex other = obj as Complex
und dann zu prüfen, obother == null
anstelle einer Verwendungis
und dann eine Besetzung ...Complex? other = obj as Complex?
, aber nullbare Typen sind oft nicht effizient.MyComplex
wie Sie vorschlagen.Sie sollten auch IEquatable <T> implementieren. Hier ist ein Auszug aus den Framework Design Guidelines:
public struct Int32 : IEquatable<Int32> { public bool Equals(Int32 other){ ... } }
quelle
Leider habe ich nicht genug Ruf, um andere Einträge zu kommentieren. Daher veröffentliche ich hier eine mögliche Verbesserung der Top-Lösung.
Korrigieren Sie mich, wenn ich falsch liege, aber die oben erwähnte Implementierung
public struct Complex { double re, im; public override bool Equals(Object obj) { return obj is Complex && this == (Complex)obj; } public override int GetHashCode() { return re.GetHashCode() ^ im.GetHashCode(); } public static bool operator ==(Complex x, Complex y) { return x.re == y.re && x.im == y.im; } public static bool operator !=(Complex x, Complex y) { return !(x == y); } }
Hat einen großen Fehler. Ich beziehe mich auf
public override int GetHashCode() { return re.GetHashCode() ^ im.GetHashCode(); }
XORing ist symmetrisch, daher würden Complex (2,1) und Complex (1,2) denselben Hashcode ergeben.
Wir sollten wahrscheinlich mehr machen wie:
public override int GetHashCode() { return re.GetHashCode() * 17 ^ im.GetHashCode(); }
quelle
Meistens können Sie die Implementierung von Equals und GetHashcode in Strukturen vermeiden, da der Compiler eine automatische Implementierung für Werttypen mit bitweisem Inhalt + Reflexion für Referenzelemente durchführt.
Schauen Sie sich diesen Beitrag an: Welches ist am besten für Datenspeicher Struct / Classes geeignet?
Zur Vereinfachung der Verwendung können Sie also weiterhin == und! = Implementieren.
Meistens können Sie jedoch die Implementierung von Equals und GetHashcode vermeiden.
Ein Fall, in dem Sie Equals und GetHashCode implementieren müssten, betrifft ein Feld, das Sie nicht berücksichtigen möchten.
Zum Beispiel ein Feld, das sich im Laufe der Zeit ändert, wie das Alter einer Person oder die InstantSpeed eines Autos (die Identität des Objekts sollte sich nicht ändern, wenn Sie es wieder im Wörterbuch an derselben Stelle finden möchten).
Grüße, bester Code
quelle
Der grundlegende Unterschied zwischen den beiden besteht darin, dass der
==
Operator statisch ist, dh die geeignete aufzurufende Methode wird zur Kompilierungszeit bestimmt, während dieEquals
Methode auf einer Instanz dinamisch aufgerufen wird.Beides zu definieren ist wahrscheinlich das Beste, auch wenn dies bei Strukturen weniger wichtig ist, da Strukturen nicht erweitert werden können (eine Struktur kann nicht von einer anderen erben).
quelle
Nur zur Vervollständigung würde ich auch raten,
Equals
Methode zu überladen :public bool Equals(Complex other) { return other.re == re && other.im == im; }
Dies ist eine echte Verbesserung, da das Eingabeargument der
Equals(Object obj)
Methode nicht in einem Boxing auftrittEinige bewährte Methoden für die Verwendung von Werttypen:
Dies kommt aus diesem Beitrag: http://theburningmonk.com/2015/07/beware-of-implicit-boxing-of-value-types/
quelle