Allgemeine Frage zu den Operatoren == & != und zu Object.Equals & Object.GetHashCode

  • C#

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

    Allgemeine Frage zu den Operatoren == & != und zu Object.Equals & Object.GetHashCode

    Hey Leute,
    ich arbeite momentan an einem Wrapper für eine Api und wollte da den Operator == einbauen. Dann kamen aber ein Fehler und zwei Warnungen:

    Quellcode

    1. ***** definiert den Operator == oder !=, aber überschreibt Object.GetHashCode() nicht.

    Quellcode

    1. ***** definiert den Operator == oder !=, aber überschreibt Object.Equals(object o) nicht.

    Quellcode

    1. Für den Operator '*****.operator ==(*****, *****)' muss außerdem ein übereinstimmender Operator '!=' definiert werden.

    Nun wollte ich euch fragen, wie ihr das alles lösen würdet. Bei den Operatoren == & != ist es ja klar, aber sollte man da eventuell einen der beiden auf das negierte Ergebnis des anderen setzen und wenn ja in welcher Richtung? Sollte Equals(a, b) nicht einfach a == b zurückgeben? Und wie macht man seine eigene GetHashCode-Funktion und ist es überhaupt nötig? Und warum müssen überhaupt beide Operatoren (== & =!) deklariert werden?
    Hoffentlich könnt ihr mir ein paar meiner Fragen beantworten :).
    MfG Stefan

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nafets3646“ ()

    Ich kenne die Unterschiede ja und was sie in welchem Fall zurückgeben. Allerdings frage ich mich, wieso ich Equals & GetHashCode implementieren soll. Außerdem weiß ich nicht, wie man es am performantesten lösen kann.

    //EDIT:
    Sagen wir mal, ich hätte diese Struktur:

    C#-Quellcode

    1. public struct A
    2. {
    3. public bool A { get; set; }
    4. public double B { get; set; }
    5. public string C { get; set; }
    6. }

    Wie würdet ihr dann die Funktionen & Operatoren implementieren?
    Operatoren treten häufig paarweise auf, auch == und !=. Da sie ja die genaue Umkehrung voneinander sind, kannst du bei != einfach !(a == b) zurückgeben, das geht schon in Ordnung.
    Die anderen beiden sind keine Pflicht, aber es empfiehlt sich. Bei Equals sollte klar sein, warum. Durch den ==-Operator definierst du einen eigenen Wertevergleich, allerdings können an manchen Stellen Operatoren nicht sicher aufgerufen werden (z.B. Interfaces, Generics), und da wird dann Equals aufgerufen, von dem aber verständlicherweise erwartet wird, das selbe Verhalten wie der ==-Operator zu haben. Standardmäßig prüft Equals aber auf Referenzgleichheit (also das selbe wie ReferenceEquals). Du solltest auch in ERwägung ziehen, IEquatable<dein Typ> zu implementieren, damit du auch eine typisierte Equals-Funktion hast.
    Den Hashcode brauchst du nur zu überschreiben, wenn du planst, Instanzen dieser Klasse in irgend welchen sortierten Auflistungen zu halten. Üblicherweise bilde ich meine Hashcodes aus XOr-Verknüpfungen der Hashcodes der Variablen. Wichtig hierbei ist aber, dass du nur diejenigen Variablen miteinbeziehst, die signifikant für den Zustand des Objektes sind. Objekte mit dem gleichen Zustand müssen auch zwingend den selben Hashcode zurückgeben, aber ich denke das sollte klar sein.

    Edit:
    Ich würde das so implementieren:
    Spoiler anzeigen

    C#-Quellcode

    1. public struct Test : IEquatable<Test>
    2. {
    3. public bool A { get; set; }
    4. public double B { get; set; }
    5. public string C { get; set; }
    6. public override bool Equals(object obj)
    7. {
    8. if (obj is Test)
    9. return Equals((Test)obj);
    10. else
    11. return false;
    12. }
    13. public bool Equals(Test other)
    14. {
    15. return this.A == other.A && this.B == other.B && this.C == other.C;
    16. }
    17. public override int GetHashCode()
    18. {
    19. return A.GetHashCode() ^ B.GetHashCode() ^ C.GetHashCode();
    20. }
    21. public static bool operator ==(Test left, Test right)
    22. {
    23. return left.Equals(right);
    24. }
    25. public static bool operator !=(Test left, Test right)
    26. {
    27. return !left.Equals(right);
    28. }
    29. }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Artentus“ ()

    @Artentus
    Wenn ich nun alles implementieren sollte, wie sähe es denn von der Performance her aus? Wäre es sinnvoller zuerst den Operator != zu machen, der dann bei allen relevanten Variablen auf == prüft, die Ergebnisse mit || verknüpft und dann das Ergebnis negiert oder zuerst den Operator == zu machen und dann bei allen relevanten Variablen auf == zu prüfen und die Ergebnisse dann mit && zu verknüpfen?
    ich finde die Compiler-Hinweise alle wichtig und befolgenswert, also dass == gleiche Ergebnisse zeigt wie .Equals, und auch dass die HashCodes zweier gleicher Instanzen sich gleichen.

    Sonst tuste iwann dein Objekt mal in ein Dictionary, und dann wunderste dich nur, wenn das System nicht ordentlich aufeinander abgestimmt ist.

    evtl. kommt noch <, > hinzu, dann steht auch Implementieren von IComparable<T> an, was man mit ==, != ja gut in Einklang bringen kann.
    Ok, danke schonmal für die Hilfe :). Ich habe aber noch ne Frage: Gibt es einen Performanceunterschied zwischen Artentus seiner Implementierung und dieser?

    C#-Quellcode

    1. public struct A : IEquatable<A>
    2. {
    3. public bool A { get; set; }
    4. public double B { get; set; }
    5. public string C { get; set; }
    6. public static bool operator ==(A left, A right)
    7. {
    8. return !(left != right);
    9. }
    10. public static bool operator !=(A left, A right)
    11. {
    12. return !((left.A == right.A) || (left.B == right.B) || (left.C == right.C));
    13. }
    14. public bool IEquatable<A>.Equals(A other)
    15. {
    16. return (other != null) && (this == other);
    17. }
    18. public override bool Equals(object obj)
    19. {
    20. return (obj is A) && (obj != null) && (this == (A)obj);
    21. }
    22. public override int GetHashCode()
    23. {
    24. return this.A.GetHashCode() ^ this.B.GetHashCode() ^ this.C.GetHashCode();
    25. }
    26. }

    So hatte ich das mal in nem Tutorial gesehen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nafets3646“ ()