Partager via


Implémentation de la méthode Equals

Pour des informations connexes sur l'implémentation de l'opérateur d'égalité (==), consultez Indications concernant l'implémentation de Equals et de l'opérateur d'égalité (==).

  • Substituez la méthode GetHashCode pour permettre à un type de fonctionner correctement dans une table de hachage.

  • Ne levez pas d'exception dans l'implémentation d'une méthode Equals. Retournez plutôt false pour un argument null.

  • Respectez le contrat défini dans la méthode Object.Equals comme suit :

    • x.Equals(x) retourne true.

    • x.Equals(y) retourne la même valeur que y.Equals(x).

    • (x.Equals(y) && y.Equals(z)) retourne true si et seulement si x.Equals(z) retourne true.

    • Les appels successifs de x.Equals(y) retournent la même valeur tant que les objets référencés par x et y ne sont pas modifiés.

    • x.Equals(null) retourne false.

  • Pour certaines sortes d'objets, il est bon que Equals teste l'égalité des valeurs au lieu de l'égalité référentielle. De telles implémentations de Equals retournent true si les deux objets ont la même valeur, même s'ils ne sont pas la même instance. Il revient à l'implémenteur du type de définir la composition de la valeur d'un objet ; cependant, un objet comporte généralement tout ou partie des données stockées dans les variables de l'instance de l'objet. Par exemple, la valeur d'une chaîne est basée sur ses caractères ; la méthode Equals de la classe String retourne true pour les deux instances d'une chaîne qui contiennent exactement les mêmes caractères, dans le même ordre.

  • Quand la méthode Equals d'une classe de base fournit l'égalité des valeurs, une substitution de Equals dans une classe dérivée doit appeler l'implémentation héritée de Equals.

  • Si vous programmez dans un langage qui prend en charge la surcharge d'opérateur et que vous choisissez de surcharger l'opérateur d'égalité (==) pour un type spécifié, celui-ci doit substituer la méthode Equals. De telles implémentations de la méthode Equals doivent retourner les mêmes résultats que l'opérateur d'égalité. Respectez cette règle afin de garantir que le fonctionnement du code de la bibliothèque de classes utilisant Equals (notamment ArrayList et Hashtable) est cohérent par rapport à la façon dont le code d'application utilise l'opérateur d'égalité.

  • Si vous implémentez un type valeur, vous devez envisager de substituer la méthode Equals afin d'améliorer les performances par rapport à l'implémentation par défaut de la méthode Equals sur ValueType. Si vous substituez Equals et que le langage prend en charge la surcharge d'opérateur, vous devez surcharger l'opérateur d'égalité pour votre type valeur.

  • Si vous implémentez les types référence, vous devez envisager de substituer la méthode Equals sur un type référence s'il s'apparente à un type de base tel que Point, String, BigNumber, etc. La plupart des types référence ne doivent pas surcharger l'opérateur d'égalité, même s'ils substituent Equals. Cependant, si vous implémentez un type référence qui doit avoir une sémantique de valeur, telle qu'un type nombre complexe, vous devez substituer l'opérateur d'égalité.

  • Si vous implémentez l'interface IComparable sur un type donné, vous devez substituer Equals sur ce type.

Exemples

Les exemples de code suivants illustrent l'implémentation, la substitution de l'appel et la surcharge de la méthode Equals.

Implémentation de la méthode Equals

L'exemple de code suivant comporte deux appels à l'implémentation par défaut de la méthode Equals.

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

Le résultat du code ci-dessus est le suivant :

False
True

Substitution de la méthode Equals

L'exemple de code suivant montre une classe Point qui substitue la méthode Equals afin de fournir l'égalité de valeur et une classe Point3D, dérivée de Point. Dans la mesure où la substitution de Equals de la classe Point est la première de la chaîne d'héritage à introduire l'égalité de valeur, la méthode Equals de la classe de base (héritée de Object et vérifiant l'égalité référentielle) n'est pas appelée. Cependant, Point3D.Equals appelle Point.Equals parce que Point implémente Equals de façon à fournir l'égalité de valeur.

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

La méthode Point.Equals vérifie que l'argument obj n'est pas null et qu'il fait référence à une instance du même type que cet objet. Si l'une de ces vérifications échoue, la méthode retourne false. La méthode Equals utilise la méthode GetType pour déterminer si les types des deux objets au moment de l'exécution sont identiques. Notez que typeof (TypeOf en Visual Basic) n'est pas utilisé ici parce qu'il retourne un type statique. Si la méthode a plutôt utilisé une vérification de type obj is Point , la vérification doit retourner true dans les cas où obj constitue l'instance d'une classe dérivée de Point, même si obj et l'instance en cours n'ont pas le même type de runtime. Après avoir vérifié que les deux objets sont du même type, la méthode effectue un cast de obj en type Point et retourne le résultat de la comparaison des variables des instances des deux objets.

Dans Point3D.Equals, la méthode Equals héritée est appelée avant toute autre action. La méthode Equals héritée vérifie que obj n'est pas null, qu'il représente une instance appartenant à la même classe que cet objet et que les variables de l'instance héritée concordent. La méthode Equals héritée ne compare les variables de l'instance introduites dans la classe dérivée qu'au cas où elle retourne true. En particulier, le cast en Point3D n'est pas exécuté tant qu'il n'a pas été déterminé que obj est du type Point3D ou est une classe dérivée de Point3D.

Utilisation de la méthode Equals pour comparer des variables d'instance

Dans l'exemple précédent, l'opérateur d'égalité (==) sert à comparer les variables d'instance individuelles. Dans certains cas, il est approprié d'utiliser la méthode Equals pour comparer des variables d'instance dans une implémentation Equals, comme le montre l'exemple de code suivant.

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

Surcharge de l'opérateur d'égalité (==) et de la méthode Equals

La surcharge d'opérateur est prise en charge dans certains langages de programmation, tels que C#. Quand un type surcharge l'opérateur d'égalité (==), il doit aussi substituer la méthode Equals pour fournir la même fonctionnalité. En général, ceci est réalisé en écrivant la méthode Equals sous la forme de l'opérateur d'égalité surchargé (==), comme dans l'exemple de code suivant.

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

Dans la mesure où Complex constitue un type struct C# (un type valeur), on sait qu'aucune classe ne sera dérivée de Complex. C'est pourquoi il est inutile que la méthode Equals compare les résultats GetType pour chaque objet. Elle utilise plutôt l'opérateur is pour vérifier le type du paramètre obj.

Portions Copyright 2005 Microsoft Corporation. Tous droits réservés.

Portions Copyright Addison-Wesley Corporation. Tous droits réservés.

Pour plus d'informations sur les règles de conception, consultez « règles de conception d'infrastructure : Conventions idiomes et modèles carnet de bibliothèques réutilisables framework » Krzysztof Cwalina et Brad Abrams, publiés par Addison-Wesley, 2005.

Voir aussi

Autres ressources

Instructions de conception pour le développement de bibliothèques de classes