wake-up-neo.net

Wie implementiere ich Equals am besten für benutzerdefinierte Typen?

Sagen wir für eine Point2-Klasse und die folgenden Werte sind gleich:

public override bool Equals ( object obj )

public bool Equals ( Point2 obj )

Dies ist die in Effective C # 3 gezeigte:

public override bool Equals ( object obj )
{
    // STEP 1: Check for null
    if ( obj == null )
    {
        return false;
    }

    // STEP 3: equivalent data types
    if ( this.GetType ( ) != obj.GetType ( ) )
    {
        return false;
    }
    return Equals ( ( Point2 ) obj );
}

public bool Equals ( Point2 obj )
{
    // STEP 1: Check for null if nullable (e.g., a reference type)
    if ( obj == null )
    {
        return false;
    }
    // STEP 2: Check for ReferenceEquals if this is a reference type
    if ( ReferenceEquals ( this, obj ) )
    {
        return true;
    }
    // STEP 4: Possibly check for equivalent hash codes
    if ( this.GetHashCode ( ) != obj.GetHashCode ( ) )
    {
        return false;
    }
    // STEP 5: Check base.Equals if base overrides Equals()
    System.Diagnostics.Debug.Assert (
        base.GetType ( ) != typeof ( object ) );

    if ( !base.Equals ( obj ) )
    {
        return false;
    }

    // STEP 6: Compare identifying fields for equality.
    return ( ( this.X.Equals ( obj.X ) ) && ( this.Y.Equals ( obj.Y ) ) );
}
30
Joan Venge

Es gibt auch eine ganze Reihe von Richtlinien zu MSDN . Sie sollten sie gut lesen, es ist sowohl schwierig als auch wichtig. 

Einige Punkte, die ich am hilfreichsten fand:

  • Werttypen haben keine Identität, daher führen Sie instruct Pointnormalerweise einen Member-Vergleich durch.

  • Referenztypen haben normalerweise eine Identität, und daher endet der Equals-Test normalerweise bei ReferenceEquals (Standardeinstellung, keine Notwendigkeit, sie zu überschreiben). Es gibt jedoch Ausnahmen wie string und Ihrclass Point2, bei denen ein Objekt keine nützliche Identität hat und Sie dann die Equality-Member überschreiben, um Ihre eigene Semantik bereitzustellen. Befolgen Sie in dieser Situation die Richtlinien, um zuerst die Nullfälle und andere Fälle zu durchlaufen.

  • Und es gibt gute Gründe, GethashCode() und operator== ebenfalls synchron zu halten. 

22
Henk Holterman

Wenn der Typ von obj Point2 ist, rufen Sie den typspezifischen Equals auf. Stellen Sie in den typspezifischen Equals sicher, dass alle Member denselben Wert haben.

public override bool Equals ( object obj )
{
   return Equals(obj as Point2);
}

public bool Equals ( Point2 obj )
{
   return obj != null && obj.X == this.X && obj.Y == this.Y ... 
   // Or whatever you think qualifies as the objects being equal.
}

Sie sollten wahrscheinlich auch GetHashCode überschreiben, um sicherzustellen, dass Objekte, die "gleich" sind, denselben Hashcode haben.

34

Die Technik, die ich verwendet habe, hat für mich funktioniert. Beachten Sie, ich vergleiche nur basierend auf einer einzelnen Eigenschaft (Id) und nicht mit zwei Werten. Passen Sie nach Bedarf an 

using System;
namespace MyNameSpace
{
    public class DomainEntity
    {
        public virtual int Id { get; set; }

        public override bool Equals(object other)
        {
            return Equals(other as DomainEntity);
        }

        public virtual bool Equals(DomainEntity other)
        {
            if (other == null) { return false; }
            if (object.ReferenceEquals(this, other)) { return true; }
            return this.Id == other.Id;
        }

        public override int GetHashCode()
        {
            return this.Id;
        }

        public static bool operator ==(DomainEntity item1, DomainEntity item2)
        {
            if (object.ReferenceEquals(item1, item2)) { return true; }
            if ((object)item1 == null || (object)item2 == null) { return false; }
            return item1.Id == item2.Id;
        }

        public static bool operator !=(DomainEntity item1, DomainEntity item2)
        {
            return !(item1 == item2);
        }
    }
}
8
user1325543
  • Definieren Sie, was die Identität bedeutet. Wenn die Referenzidentität verwendet wird, funktionieren die geerbten Standardgleichungen.
  • Wenn Sie einen Werttyp (und damit eine Identität) angeben, müssen Sie definieren.
  • Wenn ein Klassentyp aber Wertsemantik hat, definieren Sie.

Wahrscheinlich möchten Sie beide Equals (Objekt) überschreiben und Equals (MyType) definieren, da letzteres das Boxen verhindert. Und überschreiben Sie den Gleichheitsoperator.

Das .NET Framework Guidelines-Buch (2. Auflage) hat mehr Abdeckung.

2
Richard

Verwenden Sie C # 7 und das Muster is type varname ( https://docs.Microsoft.com/en-us/dotnet/csharp/language-reference/keywords/is#type ) Für ein sauberes Equals(object), das sich mit Null und der Typprüfung befasst, verwenden Sie einen der folgenden Ansätze:

// using Equals(point)
public override bool Equals(object obj) =>
    (obj is Point other) && this.Equals(other);

// using the == operator
public override bool Equals(object obj) =>
    (obj is Point other) && this == other;

Natürlich müssen Sie mindestens Folgendes implementieren:

public bool Equals(Point2 other);
public static bool operator == (Point2 lhs, Point2 rhs);
0
Brad Bodily

Lie Daniel L sagte,

public override bool Equals(object obj) {
    Point2 point = obj as Point2; // Point2? if Point2 is a struct
    return point != null && this.Equals(point);
}

public bool Equals(Point2 point) {
    ...
}
0
configurator
public override bool Equals ( object obj )
{
   // struct
   return obj  is Point2 && Equals (  ( Point2 ) value );
   // class
   //return Equals ( obj as Point2 );
}

public bool Equals ( Point2 obj )
0
baretta

Es gibt auch ein Fody-Plugin Equals.Fody , das Equals () und GetHashCode () automatisch generiert

0
mamuesstack

Leichte Varianten von Formularen, die bereits von mehreren anderen veröffentlicht wurden ...

using System;
...
public override bool Equals ( object obj ) {
   return Equals(obj as SomeClass);
}

public bool Equals ( SomeClass someInstance ) {
    return Object.ReferenceEquals( this, someInstance ) 
        || ( !Object.ReferenceEquals( someInstance, null ) 
            && this.Value == someInstance.Value );
}

public static bool operator ==( SomeClass lhs, SomeClass rhs ) {
    if( Object.ReferenceEquals( lhs, null ) ) {
        return Object.ReferenceEquals( rhs, null );
    }
    return lhs.Equals( rhs );
    //OR
    return Object.ReferenceEquals( lhs, rhs )
            || ( !Object.ReferenceEquals( lhs, null ) 
                && !Object.ReferenceEquals( rhs, null )
                && lhs.Value == rhs.Value );
}

public static bool operator !=( SomeClass lhs, SomeClass rhs ) {
    return !( lhs == rhs );
    // OR
    return ( Object.ReferenceEquals( lhs, null ) || !lhs.Equals( rhs ) )
            && !Object.ReferenceEquals( lhs, rhs );
}

Der Versuch, einen Operator zu implementieren == mit Equals, um zu vermeiden, dass die Wertvergleichslogik dupliziert wird ... ohne redundante Tests (ReferenceEquals-Aufrufe mit denselben Parametern) oder unnötige Tests (dies kann in der Instanz nicht gleich null sein.) Equals Methode) und ohne explizite Bedingungen ("ifs"). Es ist mehr ein Gedankenkostüm als alles andere nützliche.

Am nächsten kann ich mir das vorstellen, aber es fühlt sich an, als wäre es ohne eine zusätzliche Methode möglich :)

public bool Equals ( SomeClass someInstance ) {
    return Object.ReferenceEquals( this, someInstance ) 
        || (!Object.ReferenceEquals( someInstance, null ) && EqualsNonNullInstance( someInstance );
}

public static bool operator ==( SomeClass lhs, SomeClass rhs ) {
    return Object.ReferenceEquals( lhs, rhs ) 
    || ( !Object.ReferenceEquals( lhs, null ) && !Object.ReferenceEquals( rhs, null ) && lhs.EqualsNonNullInstance( rhs ) );
}

//super fragile method which returns logical non-sense
protected virtual bool EqualsNonNullInstance ( SomeClass someInstance ) {
    //In practice this would be a more complex method...
    return this.Value == someInstance.Value;
}

Erinnern wir uns, wie langwierig und fehleranfällig das alles ist (ich bin fast sicher, dass es einen Fehler in dem obigen Code gibt ... der immer noch scheiße ist, weil wer einen Typ nur) unterordnen möchte, um Gleichheitsprüfungen etwas einfacher zu machen? vorwärts Ich denke, ich werde nur einige statische Methoden erstellen, die alle Nullprüfungen handhaben und einen Delegaten akzeptieren oder ein Interface erfordern, um den Vergleich von Werten durchzuführen (der einzige Teil, der wirklich Typ in Typ ändert).

Es wäre toll, wenn wir einfach Attribute zu den Feldern/Eigenschaften/Methoden hinzufügen könnten, die verglichen werden müssen, und der Compiler/die Laufzeitumgebung das gesamte Langeweile handhaben lässt.

Stellen Sie außerdem sicher, dass die GetHashCode () - Werte für alle Fälle gleich sind, in denen .Equals (object) true oder verrückte Scheiße zurückgibt.

0
Steazy

Die einfache und beste Möglichkeit, Equals zu überschreiben, sieht folgendermaßen aus:

public class Person
    {
        public int Age { get; set; }
        public string Name { get; set; }

        public override bool Equals(object other)
        {
            Person otherItem = other as Person;

            if (otherItem == null)
                return false;

            return Age == otherItem.Age && Name == otherItem.Name;
        }
        public override int GetHashCode()
        {
            int hash = 13;
            hash = (hash * 7) + Age.GetHashCode();
            hash = (hash * 7) + Name.GetHashCode();
            return hash;
        }
    }

Überschreiben Sie die GetHashCode-Methode, damit ein Typ in einer Hashtabelle ordnungsgemäß funktionieren kann. 

0
LIDEN