Implémentation de la méthode Equals
Mise à jour : novembre 2007
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 instruction 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;
}
}
}
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 instructions de conception, consultez le livre « Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries » de Krzysztof Cwalina et Brad Abrams, publié par Addison-Wesley, 2005.
Voir aussi
Autres ressources
Instructions de conception pour le développement de bibliothèques de classes