Share via


Implementar el método Equals

Para obtener más información sobre la implementación del operador de igualdad (==), vea Instrucciones para implementar Equals y el operador de igualdad (==).

  • Reemplace el método GetHashCode para permitir que un tipo que funcione correctamente en una tabla hash.

  • No inicie una excepción en la implementación de un método Equals. En vez de esto, devuelva un valor false para un argumento nulo.

  • Siga el contrato definido en el Método Object.Equals de la forma siguiente:

    • x.Equals(x) devuelve true.

    • x.Equals(y) devuelve el mismo valor que y.Equals(x).

    • (x.Equals(y) && y.Equals(z)) devuelve el valor true si y sólo si x.Equals(z) devuelve true.

    • Las invocaciones sucesivas de x.Equals(y) devuelven el mismo valor siempre que no se modifiquen los objetos a los que se hace referencia mediante x e y.

    • x.Equals(null) devuelve false.

  • En algunas clases de objetos, es conveniente realizar una comprobación de Equals para comprobar así la igualdad de valores en lugar de la igualdad referencial. Este tipo de implementaciones de Equals devuelve el valor true si los dos objetos tienen el mismo valor, aunque no sean la misma instancia. La definición de lo que constituye el valor de un objeto está en función del implementador del tipo, pero suele ser una parte o la totalidad de los datos almacenados en las variables de la instancia del objeto. Por ejemplo, el valor de una cadena se basa en los caracteres de la cadena; el método Equals de la clase String devuelve un valor true en el caso de que dos instancias de una cadena contengan exactamente los mismos caracteres y en el mismo orden.

  • Cuando el método Equals de una clase base proporciona igualdad de valores, un reemplazo del método Equals de una clase derivada deberá llamar a la implementación heredada de Equals.

  • Si programa en un lenguaje que es compatible con la sobrecarga de operadores y elige sobrecargar el operador de igualdad (==) para un tipo especificado, este tipo deberá reemplazar el método Equals. Este tipo de implementaciones del método Equals debe devolver los mismos resultados que el operador de igualdad. Siguiendo esta instrucción se garantiza que el código de la biblioteca de clases que utiliza el método Equals (como ArrayList y Hashtable) funciona de manera coherente en cuanto a la forma en que el código de la aplicación utiliza el operador de igualdad.

  • Si implementa un tipo de valor, considere la posibilidad de reemplazar el método Equals para conseguir un mayor rendimiento que el que proporciona la implementación predeterminada del método Equals en ValueType. Si reemplaza Equals y el lenguaje es compatible con la sobrecarga de operadores, debe sobrecargar el operador de igualdad del tipo de valor.

  • Si implementa tipos de referencia, debe considerar la posibilidad de reemplazar el método Equals en un tipo de referencia si el tipo de referencia se parece a un tipo base como Point, String, BigNumber, etc. La mayoría de los tipos de referencia no deben sobrecargar el operador de igualdad, aunque reemplacen el método Equals. Sin embargo, si implementa un tipo de referencia destinado a contener semántica de valores, como un tipo de número complejo, deberá reemplazar el operador de igualdad.

  • Si implementa la interfaz IComparable en un tipo dado, deberá reemplazar el método Equals en ese tipo.

Ejemplos

Los ejemplos de código siguientes muestran cómo realizar la implementación reemplazando las llamadas y sobrecargando el método Equals.

Implementar el método Equals

El siguiente ejemplo de código contiene dos llamadas a la implementación predeterminada del método 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)); 
   }
}

El resultado del código anterior es el siguiente:

False
True

Reemplazar el método Equals

En el siguiente ejemplo de código se muestra una clase Point que reemplaza el método Equals para proporcionar igualdad de valores y una clase Point3D derivada de Point. Dado que el reemplazo de la clase Point por parte de Equals es el primero en la cadena de herencia para introducir la igualdad de valores, no se invoca el método Equals de la clase base (que se hereda de Object y comprueba la igualdad referencial). No obstante, Point3D.Equals invoca Point.Equals porque Point implementa Equals de forma que se proporciona igualdad de valores.

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

El método Point.Equals comprueba que el argumento obj no es null y que hace referencia a una instancia del mismo tipo que este objeto. Si alguna de las comprobaciones no es satisfactoria, el método devuelve el valor false. El método Equals utiliza el método GetType para determinar si los tipos en tiempo de ejecución de los dos objetos son idénticos. Observe que typeof (TypeOf en Visual Basic) no se utiliza aquí porque devuelve el tipo estático. Si en vez de esto el método ha utilizado una comprobación de la forma obj is Point , la comprobación devolverá el valor true en los casos en los que obj sea una instancia de una clase derivada de Point, aunque obj y la instancia actual no tengan el mismo tipo en tiempo de ejecución. Una vez comprobado que ambos objetos son del mismo tipo, el método convierte obj en el tipo Point y devuelve el resultado de la comparación de las variables de instancia de los dos objetos.

En Point3D.Equals, se invoca al método Equals heredado antes de realizar otras acciones. El método Equals heredado comprueba que obj no es null, que obj es una instancia de la misma clase que el objeto y que coinciden las variables de la instancia heredada. Sólo cuando el método Equals heredado devuelva el valor true, comparará el método las variables de instancia introducidas en la clase derivada. Concretamente, la conversión a Point3D no se ejecuta salvo que se haya determinado que obj es de tipo Point3D o una clase derivada de Point3D.

Utilizar el método Equals para comparar variables de instancia

En el ejemplo anterior, el operador de igualdad (==) se utiliza para comparar las variables de instancia individuales. En algunos casos, debe utilizarse el método Equals para comparar variables de instancia en una implementación de Equals, como se muestra en el siguiente ejemplo de código.

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

Sobrecargar el operador de igualdad (==) y el método Equals

Algunos lenguajes de programación, como el lenguaje C#, admiten la sobrecarga de operadores. Cuando un tipo sobrecarga el operador de igualdad (==), también debe reemplazar el método Equals para proporcionar la misma funcionalidad. Esto se consigue normalmente escribiendo el método Equals en términos del operador de igualdad (==) sobrecargado, como en el siguiente ejemplo de código.

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

Como Complex es un tipo de valor struct de C#, se sabe que no se derivará ninguna clase de Complex. Por tanto, no es necesario que el método Equals compare los resultados de GetType para cada objeto. En su lugar utiliza el operador is para comprobar el tipo del parámetro obj.

Portions Copyright 2005 Microsoft Corporation. Reservados todos los derechos.

Portions Copyright Addison-Wesley Corporation. Reservados todos los derechos.

Para obtener más información sobre las directrices de diseño, consulte “las instrucciones de diseño de Framework: Convenciones, frases realizadas y modelos para libro de bibliotecas reutilizables de .NET” de Krzysztof Cwalina y Brad Abrams, publicados por Addison-Wesley, 2005.

Vea también

Otros recursos

Instrucciones de diseño para desarrollar bibliotecas de clases