Bagikan melalui


Implementing the Equals Method

For related information on implementing the equality operator (==), see Guidelines for Implementing Equals and the Equality Operator (==).

  • Override the GetHashCode method to allow a type to work correctly in a hash table.

  • Do not throw an exception in the implementation of an Equals method. Instead, return false for a null argument.

  • Follow the contract defined on the Object.Equals Method as follows:

    • x.Equals(x) returns true.

    • x.Equals(y) returns the same value as y.Equals(x).

    • (x.Equals(y) && y.Equals(z)) returns true if and only if x.Equals(z) returns true.

    • Successive invocations of x.Equals(y) return the same value as long as the objects referenced by x and y are not modified.

    • x.Equals(null) returns false.

  • For some kinds of objects, it is desirable to have Equals test for value equality instead of referential equality. Such implementations of Equals return true if the two objects have the same value, even if they are not the same instance. The definition of what constitutes an object's value is up to the implementer of the type, but it is typically some or all of the data stored in the instance variables of the object. For example, the value of a string is based on the characters of the string; the Equals method of the String class returns true for any two instances of a string that contain exactly the same characters in the same order.

  • When the Equals method of a base class provides value equality, an override of Equals in a derived class should call the inherited implementation of Equals.

  • If you are programming in a language that supports operator overloading, and you choose to overload the equality operator (==) for a specified type, that type should override the Equals method. Such implementations of the Equals method should return the same results as the equality operator. Following this guideline will help ensure that class library code using Equals (such as ArrayList and Hashtable) works in a manner that is consistent with the way the equality operator is used by application code.

  • If you are implementing a value type, you should consider overriding the Equals method to gain increased performance over the default implementation of the Equals method on ValueType. If you override Equals and the language supports operator overloading, you should overload the equality operator for your value type.

  • If you are implementing reference types, you should consider overriding the Equals method on a reference type if your type looks like a base type, such as Point, String, BigNumber, and so on. Most reference types should not overload the equality operator, even if they override Equals. However, if you are implementing a reference type that is intended to have value semantics, such as a complex number type, you should override the equality operator.

  • If you implement the IComparable interface on a given type, you should override Equals on that type.

Examples

The following code examples demonstrate implementing, overriding calling and overloading the Equals method.

Implementing the Equals Method

The following code example contains two calls to the default implementation of the Equals method.

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

The output of the preceding code is as follows:

False
True

Overriding the Equals Method

The following code example shows a Point class that overrides the Equals method to provide value equality and a class Point3D, which is derived from Point. Because the Point class's override of Equals is the first in the inheritance chain to introduce value equality, the Equals method of the base class (which is inherited from Object and checks for referential equality) is not invoked. However, Point3D.Equals invokes Point.Equals because Point implements Equals in a manner that provides value equality.

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

The Point.Equals method checks that the obj argument is not null and that it references an instance of the same type as this object. If either of the checks fail, the method returns false. The Equals method uses the GetType Method to determine whether the run-time types of the two objects are identical. Note that typeof (TypeOf in Visual Basic) is not used here because it returns the static type. If the method had used a check of the form obj is Point instead, the check would return true in cases where obj is an instance of a class derived from Point, even though obj and the current instance are not of the same run-time type. Having verified that both objects are of the same type, the method casts obj to type Point and returns the result of comparing the instance variables of the two objects.

In Point3D.Equals, the inherited Equals method is invoked before anything else is done. The inherited Equals method verifies that obj is not null, that obj is an instance of the same class as this object, and that the inherited instance variables match. Only when the inherited Equals returns true does the method compare the instance variables introduced in the derived class. Specifically, the cast to Point3D is not executed unless obj has been determined to be of type Point3D or a class derived from Point3D.

Using the Equals Method to Compare Instance Variables

In the previous example, the equality operator (==) is used to compare the individual instance variables. In some cases, it is appropriate to use the Equals method to compare instance variables in an Equals implementation, as shown in the following code example.

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

Overloading the Equality Operator (==) and the Equals Method

In some programming languages, such as C#, operator overloading is supported. When a type overloads the equality operator (==), it should also override the Equals method to provide the same functionality. This is typically accomplished by writing the Equals method in terms of the overloaded equality operator (==), as in the following code example.

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

Because Complex is a C# struct (a value type), it is known that no classes will be derived from Complex. Therefore, the Equals method does not need to compare the GetType results for each object. Instead it uses the is operator to check the type of the obj parameter.

Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

For more information on design guidelines, see the "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" book by Krzysztof Cwalina and Brad Abrams, published by Addison-Wesley, 2005.

See Also

Other Resources

Design Guidelines for Developing Class Libraries