Implementazione del metodo Equals

Per informazioni correlate sull'implementazione dell'operatore di uguaglianza (==), vedere Linee guida per l'implementazione del metodo Equals e dell'operatore di uguaglianza (==).

  • Eseguire l'override del metodo GetHashCode per consentire a un tipo di funzionare correttamente in una tabella hash.

  • Non generare un'eccezione nell'implementazione di un metodo Equals. Restituire invece false per un argomento null.

  • Seguire il contratto definito nel metodo Object.Equals nel modo seguente:

    • x.Equals(x) restituisce true.

    • x.Equals(y) restituisce lo stesso valore di y.Equals(x).

    • (x.Equals(y) && y.Equals(z)) restituisce true se e solo se x.Equals(z) restituisce true.

    • Le chiamate successive a x.Equals(y) restituiscono lo stesso valore purché non vengano modificati gli oggetti cui fanno riferimento x e y.

    • x.Equals(null) restituisce false.

  • Per alcuni tipi di oggetti, è preferibile che il metodo Equals esegua il test di uguaglianza dei valori anziché dei riferimenti. Tali implementazioni del metodo Equals restituiscono true se i due oggetti hanno lo stesso valore, anche se non sono la stessa istanza. La funzione di implementazione del tipo definisce ciò che costituisce il valore di un oggetto ma in genere si tratta di alcuni o tutti i dati memorizzati nelle variabili di istanza dell'oggetto. Il valore di una stringa, ad esempio, si basa sui caratteri della stringa. Il metodo Equals della classe String restituisce true per due istanze di una stringa che contengono esattamente gli stessi caratteri nello stesso ordine.

  • Quando il metodo Equals di una classe base fornisce l'uguaglianza dei valori, un override del metodo Equals in una classe derivata deve chiamare l'implementazione ereditata del metodo Equals.

  • Se il linguaggio di programmazione utilizzato supporta l'overload dell'operatore e si sceglie di eseguire l'overload dell'operatore di uguaglianza (==) per un determinato tipo, tale tipo deve eseguire l'override del metodo Equals. Tali implementazioni del metodo Equals devono restituire lo stesso risultato dell'operatore di uguaglianza. Queste linee guida contribuiscono a garantire che il funzionamento del codice della libreria di classi che utilizza il metodo Equals, ad esempio ArrayList e Hashtable, sia coerente con la modalità di utilizzo dell'operatore di uguaglianza da parte del codice dell'applicazione.

  • Quando si implementa un tipo di valore, è consigliabile eseguire l'override del metodo Equals per aumentare le prestazioni rispetto all'implementazione predefinita del metodo Equals su ValueType. Se si esegue l'override di Equals e il linguaggio supporta l'overload dell'operatore, è necessario eseguire l'overload dell'operatore di uguaglianza per il tipo di valore utilizzato.

  • Se si implementano tipi di riferimento, è necessario eseguire l'override del metodo Equals su un tipo di riferimento se il tipo utilizzato appare come un tipo base, ad esempio Point, String, BigNumber e così via. La maggior parte dei tipi di riferimento non deve eseguire l'overload dell'operatore di uguaglianza anche se esegue l'override di Equals. Se tuttavia si implementa un tipo di riferimento che presumibilmente dispone di una semantica di valori, ad esempio un tipo di numero complesso, è necessario eseguire l'override dell'operatore di uguaglianza.

  • Se si implementa l'interfaccia IComparable su un determinato tipo, è necessario eseguire l'override di Equals su tale tipo.

Esempi

Nell'esempio di codice riportato di seguito vengono illustrate le procedure di implementazione, override della chiamata e overload del metodo Equals.

Implementazione del metodo Equals

Nell'esempio di codice riportato di seguito sono contenute due chiamate all'implementazione predefinita del metodo 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)); 
   }
}

Di seguito è riportato l'output del codice precedente:

False
True

Esecuzione dell'override del metodo Equals

Nell'esempio di codice seguente vengono descritte una classe Point che esegue l'override del metodo Equals per fornire l'uguaglianza dei valori e una classe Point3D derivata da Point. Poiché l'override del metodo Equals da parte della classe Point è il primo elemento della catena di ereditarietà a introdurre l'uguaglianza dei valori, il metodo Equals della classe base, ereditata da Object e utilizzata per controllare l'uguaglianza dei riferimenti, non viene chiamato. Point3D.Equals chiama tuttavia il metodo Point.Equals poiché Point implementa Equals in modo tale da fornire l'uguaglianza dei valori.

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

Il metodo Point.Equals controlla che l'argomento obj non sia null e che faccia riferimento a un'istanza dello stesso tipo dell'oggetto. Se uno di tali controlli ha esito negativo, il metodo restituisce false. Il metodo Equals utilizza il metodo GetType per determinare se i tipi in fase di esecuzione dei due oggetti sono identici. Si noti che in questo caso typeof (TypeOf in Visual Basic) non viene utilizzato in quanto restituisce il tipo static. Se invece il metodo ha utilizzato un controllo del form obj is Point , il controllo restituirà true nei casi in cui obj rappresenta un'istanza di una classe derivata da Point, anche se obj e l'istanza corrente non sono dello stesso tipo in fase di esecuzione. Una volta verificato che entrambi gli oggetti sono dello stesso tipo, il metodo esegue il cast di obj al tipo Point e restituisce il risultato del confronto tra le variabili di istanza dei due oggetti.

In Point3D.Equals, il metodo Equals ereditato viene chiamato prima di eseguire qualsiasi altra operazione. Il metodo Equals ereditato controlla che obj non sia null, che sia un'istanza della stessa classe di questo oggetto e che le variabili di istanza ereditate corrispondano. Solo quando il metodo Equals ereditato restituisce true il metodo confronta le variabili di istanza introdotte nella classe derivata. In particolare, il cast a Point3D non viene eseguito se non è stato stabilito che obj è di tipo Point3D o una classe derivata da Point3D.

Utilizzo del metodo Equals per confrontare le variabili di istanza

Nell'esempio precedente, viene utilizzato l'operatore di uguaglianza (==) per confrontare le singole variabili di istanza. In alcuni casi è opportuno utilizzare il metodo Equals per confrontare le variabili di istanza in un'implementazione di Equals, come illustrato nell'esempio di codice riportato di seguito.

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

Esecuzione dell'overload dell'operatore di uguaglianza (==) e del metodo Equals

In alcuni linguaggi di programmazione, ad esempio C#, l'overload dell'operatore è supportato. Quando un tipo esegue l'overload dell'operatore di uguaglianza (==), per fornire la stessa funzionalità è necessario che venga eseguito anche l'override del metodo Equals. Questo risultato in genere si ottiene scrivendo il metodo Equals nei termini dell'operatore di uguaglianza (==) sottoposto a overload, come nell'esempio di codice riportato di seguito.

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

Poiché Complex è un tipo struct C# (un tipo di valore), nessuna classe potrà derivare da Complex. Non è necessario quindi che il metodo Equals confronti i risultati di GetType per ciascun oggetto. Per controllare il tipo del parametro obj viene utilizzato invece l'operatore is.

Portions Copyright 2005 Microsoft Corporation. Tutti i diritti riservati.

Portions Copyright Addison-Wesley Corporation. Tutti i diritti riservati.

Per ulteriori informazioni sulle linee guida di progettazione, vedere “le linee guida di progettazione di Framework: Idiomi convenzioni, e modelli per libro raccolte riutilizzabili .NET„ di Krzysztof Cwalina e brad Abrams, emessi da Addison-Wesley, 2005.

Vedere anche

Altre risorse

Linee guida di progettazione per lo sviluppo di librerie di classi