Richtlinien zum Überschreiben von Equals() und des Operators == (C#-Programmierhandbuch)

Aktualisiert: November 2007

In C# gibt es zwei verschiedene Arten von Gleichheit: Verweisgleichheit und Wertgleichheit. Wertgleichheit entspricht der üblichen Bedeutung von Gleichheit: Zwei Objekte enthalten die gleichen Werte. Zum Beispiel haben zwei ganze Zahlen mit dem Wert 2 Wertgleichheit. Verweisgleichheit bedeutet, dass keine zwei vergleichbaren Objekte vorhanden sind. Stattdessen gibt es zwei Objektverweise, die beide auf dasselbe Objekt verweisen. Dies kann durch einfache Zuweisung auftreten, wie im folgenden Beispiel gezeigt:

System.Object a = new System.Object();
System.Object b = a;
System.Object.ReferenceEquals(a, b);  //returns true

In diesem Code ist nur ein Objekt vorhanden, aber es gibt mehrere Verweise auf dieses Objekt: a und b. Da beide auf dasselbe Objekt verweisen, weisen sie Verweisgleichheit auf. Wenn zwei Objekte über Verweisgleichheit verfügen, besteht zwischen ihnen auch Wertgleichheit, allerdings garantiert Wertgleichheit keine Verweisgleichheit.

Mit ReferenceEquals können Sie auf Verweisgleichheit prüfen. Um auf Wertgleichheit zu prüfen, verwenden Sie Equals.

Überschreiben von Equals

Da Equals eine virtuelle Methode ist, kann jede Klasse ihre Implementierung überschreiben. Jede Klasse, die einen Wert (unabhängig vom Werttyp) oder eine Reihe von Werten als Gruppe (z. B. eine komplexe Zahlenklasse) repräsentiert, sollte Equals überschreiben. Wenn der Typ IComparable implementiert, muss Equals überschrieben werden.

Die neue Implementierung von Equals muss alle Garantien von Equals erfüllen:

  • x.Equals(x) gibt true zurück.

  • x. Equals (y) gibt den gleichen Wert zurück wie y. Equals (x).

  • Wenn (x. Equals (y) && y. Equals (z)) true zurückgibt, dann gibt x. Equals (z) true zurück.

  • Aufeinanderfolgende Aufrufe von x. Equals (y) geben den gleichen Wert zurück, vorausgesetzt, die Objekte, auf die x und y verweisen, werden nicht verändert.

  • x Equals (NULL) gibt false zurück (nur für nicht auf NULL festlegbare Werttypen). Weitere Informationen finden Sie unter Typen, die NULL-Werte zulassen (C#-Programmierhandbuch).

Die neue Implementierung von Equals darf keine Ausnahmen auslösen. Es wird empfohlen, dass jede Klasse, die Equals überschreibt, auch Object.GetHashCode überschreiben sollte. Außerdem sollte jede Klasse ergänzend zur Implementierung von Equals (Objekt) auch Equals (Typ) für ihre eigenen Typen implementieren, um die Leistung zu verbessern. Beispiel:

class TwoDPoint : System.Object
{
    public readonly int x, y;

    public TwoDPoint(int x, int y)  //constructor
    {
        this.x = x;
        this.y = y;
    }

    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Point return false.
        TwoDPoint p = obj as TwoDPoint;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public bool Equals(TwoDPoint p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public override int GetHashCode()
    {
        return x ^ y;
    }
}

Jede abgeleitete Klasse, die Equals auf der Basisklasse aufrufen kann, sollte dies vor Beenden des Vergleichs tun. Im folgenden Beispiel ruft Equals die Equals-Basisklasse auf, die auf einen NULL-Parameter prüft und den Parametertyp mit dem Typ der abgeleiteten Klasse vergleicht. Damit fällt der Equals-Implementierung auf der abgeleiteten Klasse die Aufgabe zu, das neue Datenfeld zu prüfen, das auf der abgeleiteten Klasse deklariert ist:

class ThreeDPoint : TwoDPoint
{
    public readonly int z;

    public ThreeDPoint(int x, int y, int z)
        : base(x, y)
    {
        this.z = z;
    }

    public override bool Equals(System.Object obj)
    {
        // If parameter cannot be cast to ThreeDPoint return false:
        ThreeDPoint p = obj as ThreeDPoint;
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return base.Equals(obj) && z == p.z;
    }

    public bool Equals(ThreeDPoint p)
    {
        // Return true if the fields match:
        return base.Equals((TwoDPoint)p) && z == p.z;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode() ^ z;
    }
}

Überschreiben des Operators ==

Standardmäßig prüft der Operator == auf Verweisgleichheit, indem er ermittelt, ob zwei Verweise dasselbe Objekt angeben. Deshalb müssen Referenztypen keinen Operator == implementieren, um diese Funktionalität zu erhalten. Wenn ein Typ unveränderlich ist, d. h., wenn die in der Instanz enthaltenen Daten nicht geändert werden können, kann ein Überladen des Operators == zum Prüfen der Wertgleichheit statt der Verweisgleichheit nützlich sein. Der Grund dafür ist, dass bei unveränderlichen Objekten davon ausgegangen werden kann, dass sie identisch sind, wenn sie identische Werte haben. Es wird davon abgeraten, den Operator == in nicht unveränderlichen Typen zu überschreiben.

Implementierungen von überladenen Operatoren == dürfen keine Ausnahmen auslösen. Jeder Typ, der den Operator == überlädt, muss auch den Operator != überladen. Beispiel:

//add this code to class ThreeDPoint as defined previously
//
public static bool operator ==(ThreeDPoint a, ThreeDPoint b)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(a, b))
    {
        return true;
    }

    // If one is null, but not both, return false.
    if (((object)a == null) || ((object)b == null))
    {
        return false;
    }

    // Return true if the fields match:
    return a.x == b.x && a.y == b.y && a.z == b.z;
}

public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
    return !(a == b);
}
Hinweis:

Ein häufiger Fehler bei der Überladung des Operators == ist die Verwendung von (a == b), (a == null) oder (b == null), um auf Verweisgleichheit zu überprüfen. Dies erstellt stattdessen einen Aufruf des überladenen Operators == und verursacht eine Endlosschleife. Um dies zu vermeiden, verwenden Sie ReferenceEquals, oder wandeln Sie den Typ in Object um.

Siehe auch

Konzepte

C#-Programmierhandbuch

Referenz

Anweisungen, Ausdrücke und Operatoren (C#-Programmierhandbuch)

Operatoren (C#-Programmierhandbuch)

Überladbare Operatoren (C#-Programmierhandbuch)

Operator == (C#-Referenz)

Operator () (C#-Referenz)