Implementieren der Equals-Methode

Weitere Informationen zur Implementierung des Gleichheitsoperators (==) finden Sie unter Richtlinien für die Verwendung der Equals-Methode und des Gleichheitsoperators (==).

  • Überschreiben Sie die GetHashCode-Methode, damit ein Typ in einer Hashtabelle korrekt funktioniert.

  • Lösen Sie in der Implementierung einer Equals-Methode keine Ausnahme aus. Geben Sie stattdessen für ein Null-Argument false zurück.

  • Wenden Sie die Richtlinien für die Object.Equals-Methode wie folgt an:

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

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

    • (x.Equals(y) && y.Equals(z)) gibt true zurück, wenn und nur wenn x.Equals(z) true zurückgibt.

    • Aufeinander folgende 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.

  • Bei bestimmten Arten von Objekten ist es wünschenswert, dass Equals auf die Gleichheit der Werte und nicht auf die Verweisgleichheit testet. Solche Implementierungen von Equals geben true zurück, wenn die beiden Objekte denselben Wert haben, auch wenn sie nicht dieselbe Instanz sind. Die Definition dessen, worin der Wert eines Objekts besteht, obliegt dem Implementierer des Typs. In der Regel sind dies jedoch einige oder alle Daten, die in den Instanzvariablen des Objekts gespeichert werden. Beispielsweise beruht der Wert einer Zeichenfolge auf den Zeichen der Zeichenfolge. Die Equals-Methode der String-Klasse gibt true zurück, wenn zwei Instanzen einer Zeichenfolge genau dieselben Zeichen in derselben Reihenfolge enthalten.

  • Wenn die Equals-Methode einer Basisklasse Wertgleichheit bereitstellt, sollte durch Überschreiben von Equals in einer abgeleiteten Klasse die geerbte Implementierung von Equals aufgerufen werden.

  • Wenn Sie in einer Sprache programmieren, die das Überladen von Operatoren unterstützt, und Sie den Gleichheitsoperator (==) für einen angegebenen Typ überladen möchten, sollte dieser Typ die Equals-Methode überschreiben. Solche Implementierungen der Equals-Methode sollten dasselbe Ergebnis wie der Gleichheitsoperator zurückgeben. Durch Einhalten dieser Richtlinie stellen Sie sicher, dass der Equals verwendende Klassenbibliothekscode (z. B. ArrayList und Hashtable) auf eine Weise ausgeführt wird, die der Verwendung des Gleichheitsoperators durch Anwendungscode entspricht.

  • Wenn Sie einen Werttyp implementieren, empfiehlt es sich u. U., die Equals-Methode zu überschreiben, um im Vergleich zur Standardimplementierung der Equals-Methode für ValueType eine Leistungssteigerung zu erzielen. Wenn Sie Equals überschreiben und die Programmiersprache das Überladen von Operatoren unterstützt, sollten Sie den Gleichheitsoperator für den Werttyp überladen.

  • Bei der Implementierung von Verweistypen empfiehlt es sich u. U., die Equals-Methode für einen Verweistyp zu überschreiben, wenn der Typ einem Basistyp, z. B. Point, String, BigNumber usw., entspricht. Die meisten Verweistypen sollten den Gleichheitsoperator nicht überladen, auch wenn sie Equals überschreiben. Wenn Sie allerdings einen Verweistyp implementieren, der Wertsemantik besitzen soll, z. B. einen Typ, der komplexe Zahlen darstellt, sollten Sie den Gleichheitsoperator überschreiben.

  • Wenn Sie die IComparable-Schnittstelle für einen bestimmten Typ implementieren, überschreiben Sie Equals für diesen Typ.

Beispiele

In den folgenden Codebeispielen wird das Implementieren, Überschreiben, Aufrufen und Überladen der Equals-Methode veranschaulicht.

Implementieren der Equals-Methode

Das folgende Codebeispiel enthält zwei Aufrufe der Standardimplementierung der Equals-Methode.

Imports System
Class SampleClass   
   Public Shared Sub Main()
      Dim obj1 As New System.Object()
      Dim obj2 As New System.Object()
      Console.WriteLine(obj1.Equals(obj2))
      obj1 = obj2
      Console.WriteLine(obj1.Equals(obj2))
   End Sub
End Class
using System;
class SampleClass 
{
   public static void Main() 
   {
      Object obj1 = new Object();
      Object obj2 = new Object();
      Console.WriteLine(obj1.Equals(obj2));
      obj1 = obj2; 
      Console.WriteLine(obj1.Equals(obj2)); 
   }
}

Der obige Code gibt Folgendes aus:

False
True

Überschreiben der Equals-Methode

Das folgende Codbeispiel veranschaulicht eine Point-Klasse, in der die Equals-Methode zum Bereitstellen von Wertgleichheit überschrieben wird, sowie die eine Point3D-Klasse, die von Point abgeleitet ist. Da das Überschreiben von Equals durch die Point-Klasse als Erstes in der Vererbungskette die Wertgleichheit einführt, wird die Equals-Methode der Basisklasse (die von Object erbt und auf Verweisgleichheit prüft) nicht aufgerufen. Point3D.Equals ruft allerdings Point.Equals auf, da Point die Equals-Methode so implementiert, dass Wertgleichheit besteht.

Namespace Examples.DesignGuidelines.EqualsImplementation

Public Class Point  
   Protected x As Integer
   Protected y As Integer

   Public Sub New (xValue As Integer, yValue As Integer)
    Me.x = xValue
    Me.y = yValue
   End Sub

   Public Overrides Overloads Function Equals(obj As Object) As Boolean

      If obj Is Nothing OrElse Not Me.GetType() Is obj.GetType() Then
         Return False
      End If

      Dim p As Point = CType(obj, Point)
      Return Me.x = p.x And Me.y = p.y
   End Function 

   Public Overrides Function GetHashCode() As Integer
      Return x Xor y
   End Function 
End Class 

Public Class Point3D
   Inherits Point
   Private z As Integer

   Public Sub New (xValue As Integer, yValue As Integer, zValue As Integer)
      MyBase.New(xValue, yValue)
      Me.z = zValue
   End Sub

   Public Overrides Overloads Function Equals(obj As Object) As Boolean
      Return MyBase.Equals(obj) And z = CType(obj, Point3D).z
   End Function 

   Public Overrides Function GetHashCode() As Integer
      Return MyBase.GetHashCode() Xor z
   End Function 
End Class 

End Namespace

using System;

namespace Examples.DesignGuidelines.EqualsImplementation
{
class Point: object 
{
   protected int x, y;

   public Point(int xValue, int yValue)
   {
        x = xValue;
        y = yValue;
   }
   public override bool Equals(Object obj) 
   {
      // Check for null values and compare run-time types.
      if (obj == null || GetType() != obj.GetType()) 
         return false;

      Point p = (Point)obj;
      return (x == p.x) && (y == p.y);
   }
   public override int GetHashCode() 
   {
      return x ^ y;
   }
}

class Point3D: Point 
{
   int z;

   public Point3D(int xValue, int yValue, int zValue) : base(xValue, yValue)
   {
        z = zValue;
   }
   public override bool Equals(Object obj) 
   {
      return base.Equals(obj) && z == ((Point3D)obj).z;
   }
   public override int GetHashCode() 
   {
      return base.GetHashCode() ^ z;
   }
}
}
using namespace System;

namespace Examples { namespace DesignGuidelines { namespace EqualsImplementation
{
    ref class Point : Object
    {
    protected:
        int x, y;

    public:
        Point(int xValue, int yValue)
        {
            x = xValue;
            y = yValue;
        }

        virtual bool Equals(Object^ obj) override
        {
            // Check for null values and compare run-time types.
            if (obj == nullptr || GetType() != obj->GetType())
            {
                return false;
            }

            Point^ p = (Point^)obj;

            return (x == p->x) && (y == p->y);
       }

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

    ref class Point3D : Point
    {
    private:
        int z;

    public:
        Point3D(int xValue, int yValue, int zValue) : Point(xValue, yValue)
        {
            z = zValue;
        }

        virtual bool Equals(Object^ obj) override
        {
            return Point::Equals(obj) && z == ((Point3D^)obj)->z;
        }

        virtual int GetHashCode() override
        {
            return Point::GetHashCode() ^ z;
        }
    };
}}}

Die Point.Equals-Methode überprüft, ob das obj-Argument nicht NULL ist und auf eine Instanz desselben Typs wie dieses Objekt verweist. Wenn eine dieser Überprüfungen negativ verläuft, gibt die Methode false zurück. Die Equals-Methode ermittelt mithilfe der GetType-Methode, ob die Laufzeittypen der beiden Objekte identisch sind. Beachten Sie, dass typeof (TypeOf in Visual Basic) hier nicht verwendet wird, da es den statischen Typ zurückgibt. Wenn die Methode stattdessen eine Überprüfung der Form obj is Point verwendet, gibt die Überprüfung true zurück, falls obj eine Instanz einer von Point abgeleiteten Klasse ist, selbst wenn obj und die aktuelle Instanz nicht den gleichen Laufzeittyp aufweisen. Nachdem bestätigt wurde, dass beide Objekte denselben Typ aufweisen, wandelt die Methode obj in den Typ Point um und gibt das Ergebnis des Vergleichs zwischen den Instanzvariablen der beiden Objekte zurück.

In Point3D.Equals wird die geerbte Equals-Methode aufgerufen, bevor irgendeine andere Aktion ausgeführt wird. Die geerbte Equals-Methode stellt sicher, dass obj nicht null ist, dass obj eine Instanz der gleichen Klasse wie dieses Objekt ist, und dass die geerbten Instanzvariablen übereinstimmen. Nur wenn die geerbte Equals-Methode true zurückgibt, vergleicht die Methode die in der abgeleiteten Klasse eingeführten Instanzvariablen. Insbesondere wird die Typumwandlung in Point3D erst ausgeführt, wenn bestimmt wurde, dass obj vom Typ Point3D ist oder eine von Point3D abgeleitete Klasse ist.

Verwenden der Equals-Methode zum Vergleichen von Instanzvariablen

Im obigen Beispiel werden die einzelnen Instanzvariablen anhand des Gleichheitsoperators (==) miteinander verglichen. In bestimmten Fällen ist es angebracht, die Instanzvariablen in einer Equals-Implementierung mithilfe der Equals-Methode zu vergleichen, wie im folgenden Codebeispiel veranschaulicht.

Imports System

Class Rectangle
   Private a, b As Point
   
   Public Overrides Overloads Function Equals(obj As [Object]) As Boolean
      If obj Is Nothing Or Not Me.GetType() Is obj.GetType() Then
         Return False
      End If
      Dim r As Rectangle = CType(obj, Rectangle)
      ' Use Equals to compare instance variables.
      Return Me.a.Equals(r.a) And Me.b.Equals(r.b)
   End Function 
   
   Public Overrides Function GetHashCode() As Integer
      Return a.GetHashCode() ^ b.GetHashCode()
   End Function 
End Class 
using System;
class Rectangle 
{
   Point a, b;
   public override bool Equals(Object obj) 
   {
      if (obj == null || GetType() != obj.GetType()) return false;
      Rectangle r = (Rectangle)obj;
      // Use Equals to compare instance variables.
      return a.Equals(r.a) && b.Equals(r.b);
   }
   public override int GetHashCode() 
   {
      return a.GetHashCode() ^ b.GetHashCode();
   }
}

Überladen des Gleichheitsoperators (==) und der Equals-Methode

Bestimmte Programmiersprachen, z. B. C#, unterstützen das Überladen von Operatoren. Wenn ein Typ den Gleichheitsoperator (==) überlädt, sollte er außerdem die Equals-Methode überschreiben, um die gleiche Funktionalität bereitzustellen. Dies geschieht i. d. R., indem die Equals-Methode wie im folgenden Codebeispiel als überladener Gleichheitsoperator (==) geschrieben wird.

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);
   }
}

Da es sich bei Complex um ein C#-struct (ein Werttyp) handelt, ist bekannt, dass keine Klassen von Complex abgeleitet werden. Daher muss die Equals-Methode nicht die GetType-Ergebnisse für jedes einzelne Objekt vergleichen. Stattdessen überprüft sie anhand des Operators is den Typ des obj-Parameters.

Copyright für einzelne Teile 2005 Microsoft Corporation. Alle Rechte vorbehalten.

Copyright für einzelne Teile Addison-Wesley Corporation. Alle Rechte vorbehalten.

Weitere Informationen zu Entwurfsrichtlinien finden Sie unter „Framework-Entwurfs-Richtlinien: Idiome, Konventionen und Muster für wiederverwendbare .NET-Bibliotheken von Krzysztof Cwalina“ book und Brad Abrams, veröffentlicht von Addison-Wesley, 2005.

Siehe auch

Weitere Ressourcen

Entwurfsrichtlinien zum Entwickeln von Klassenbibliotheken