Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Quando você define uma classe ou estrutura, você decidir se ele faz sentido criar uma definição personalizada de igualdade de valor (ou equivalência) para o tipo. Normalmente, você implementar a igualdade do valor quando objetos do tipo devem ser adicionados a uma coleção de algum tipo, ou quando sua finalidade principal é armazenar um conjunto de campos ou propriedades. Você pode basear sua definição de igualdade do valor em uma comparação de todos os campos e propriedades no tipo ou você pode basear a definição de um subconjunto. Mas, em ambos os casos e em classes e estruturas, sua implementação deve seguir as garantias de equivalência de cinco:
x.Equals(x) retorna true. é chamado propriedade reflexivo.
x.Equals(y) retorna o mesmo valor de y.Equals(x). Isso se chama a propriedade simétrica.
Se (x.Equals(y) & & y.Equals(z)) retorna true, em seguida, x.Equals(z) returns true. Isso se chama a propriedade transitiva.
Invocações sucessivas de x.Equalso mesmo valor, desde que os objetos referenciados pelo x e y não são modificados de retorno de (y).
x.Equals(nulo) retorna false. No entanto, nulo.Equals(null) lança uma exceção; ele não obedecer a regra número dois acima.
Qualquer estrutura que você definir já tem uma implementação padrão de igualdade do valor que ele herda o System.ValueType Substituir da Object.Equals(Object) método. Essa implementação usa a reflexão para examinar todos os campos públicos e não-públicos e propriedades do tipo. Embora essa implementação produz resultados corretos, é relativamente lenta em comparação com uma implementação personalizada que você escrever especificamente para o tipo.
Os detalhes da implementação de igualdade de valor são diferentes das classes e estruturas. No entanto, as classes e estruturas requerem as mesmas etapas básicas para a implementação de igualdade:
Substituir o virtual Object.Equals(Object) método. Na maioria dos casos, sua implementação de bool Equals( object obj ) , devem simplesmente chamar específicos do tipo Equals método é a implementação da System.IEquatable<T> interface. (Consulte a etapa 2).
Implementar a System.IEquatable<T> interface, fornecendo um tipo específico Equals método. Isso é onde a comparação de equivalência real é realizada. Por exemplo, você pode decidir definir igualdade comparando-se apenas um ou dois campos seu tipo. Não jogue exceções a partir de Equals. Classes: Esse método deve examinar apenas os campos são declarados na classe. Ela deve chamar base.Equals para examinar os campos que estão com a classe de base. (Não faça isso se o tipo herda diretamente da Object, porque o Object a implementação de Object.Equals(Object) executa uma verificação de igualdade de referência.)
Opcional, mas recomendável: Sobrecarregar o = = e ! = operadores.
Substituir Object.GetHashCode para que os dois objetos que possuem a igualdade do valor de produzir o mesmo código de hash.
Opcional: Para suportar as definições para "maior que" ou "less than", implementar a IComparable<T> para o seu tipo de interface e também sobrecarregar o < = e > = operadores.
O primeiro exemplo a segue mostra uma implementação de classe. O segundo exemplo mostra uma implementação de struct.
Exemplo
O exemplo a seguir mostra como implementar a igualdade do valor em uma classe (tipo de referência).
namespace ValueEquality
{
using System;
class TwoDPoint : IEquatable<TwoDPoint>
{
// Readonly auto-implemented properties.
public int X { get; private set; }
public int Y { get; private set; }
// Set the properties in the constructor.
public TwoDPoint(int x, int y)
{
if ((x < 1) || (x > 2000) || (y < 1) || (y > 2000))
throw new System.ArgumentException("Point must be in range 1 - 2000");
this.X = x;
this.Y = y;
}
public override bool Equals(object obj)
{
return this.Equals(obj as TwoDPoint);
}
public bool Equals(TwoDPoint p)
{
// If parameter is null, return false.
if (Object.ReferenceEquals(p, null))
{
return false;
}
// Optimization for a common success case.
if (Object.ReferenceEquals(this, p))
{
return true;
}
// If run-time types are not exactly the same, return false.
if (this.GetType() != p.GetType())
return false;
// Return true if the fields match.
// Note that the base class is not invoked because it is
// System.Object, which defines Equals as reference equality.
return (X == p.X) && (Y == p.Y);
}
public override int GetHashCode()
{
return X * 0x00010000 + Y;
}
public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs)
{
// Check for null on left side.
if (Object.ReferenceEquals(lhs, null))
{
if (Object.ReferenceEquals(rhs, null))
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return lhs.Equals(rhs);
}
public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs)
{
return !(lhs == rhs);
}
}
// For the sake of simplicity, assume a ThreeDPoint IS a TwoDPoint.
class ThreeDPoint : TwoDPoint, IEquatable<ThreeDPoint>
{
public int Z { get; private set; }
public ThreeDPoint(int x, int y, int z)
: base(x, y)
{
if ((z < 1) || (z > 2000))
throw new System.ArgumentException("Point must be in range 1 - 2000");
this.Z = z;
}
public override bool Equals(object obj)
{
return this.Equals(obj as ThreeDPoint);
}
public bool Equals(ThreeDPoint p)
{
// If parameter is null, return false.
if (Object.ReferenceEquals(p, null))
{
return false;
}
// Optimization for a common success case.
if (Object.ReferenceEquals(this, p))
{
return true;
}
// Check properties that this class declares.
if (Z == p.Z)
{
// Let base class check its own fields
// and do the run-time type comparison.
return base.Equals((TwoDPoint)p);
}
else
return false;
}
public override int GetHashCode()
{
return (X * 0x100000) + (Y * 0x1000) + Z;
}
public static bool operator ==(ThreeDPoint lhs, ThreeDPoint rhs)
{
// Check for null.
if (Object.ReferenceEquals(lhs, null))
{
if (Object.ReferenceEquals(lhs, null))
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles the case of null on right side.
return lhs.Equals(rhs);
}
public static bool operator !=(ThreeDPoint lhs, ThreeDPoint rhs)
{
return !(lhs == rhs);
}
}
class Program
{
static void Main(string[] args)
{
ThreeDPoint pointA = new ThreeDPoint(3, 4, 5);
ThreeDPoint pointB = new ThreeDPoint(3, 4, 5);
ThreeDPoint pointC = null;
int i = 5;
Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
Console.WriteLine("null comparison = {0}", pointA.Equals(pointC));
Console.WriteLine("Compare to some other type = {0}", pointA.Equals(i));
TwoDPoint pointD = null;
TwoDPoint pointE = null;
Console.WriteLine("Two null TwoDPoints are equal: {0}", pointD == pointE);
pointE = new TwoDPoint(3, 4);
Console.WriteLine("(pointE == pointA) = {0}", pointE == pointA);
Console.WriteLine("(pointA == pointE) = {0}", pointA == pointE);
Console.WriteLine("(pointA != pointE) = {0}", pointA != pointE);
System.Collections.ArrayList list = new System.Collections.ArrayList();
list.Add(new ThreeDPoint(3, 4, 5));
Console.WriteLine("pointE.Equals(list[0]): {0}", pointE.Equals(list[0]));
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
}
/* Output:
pointA.Equals(pointB) = True
pointA == pointB = True
null comparison = False
Compare to some other type = False
Two null TwoDPoints are equal: True
(pointE == pointA) = False
(pointA == pointE) = False
(pointA != pointE) = True
pointE.Equals(list[0]): False
*/
}
Em classes (tipos de referência), a implementação do padrão de ambos Object.Equals(Object) métodos realiza uma comparação de igualdade de referência, não um valor igualdade seleção. Quando um implementador substitui o método virtual, o objetivo é dar a semântica de igualdade do valor.
O == e != operadores podem ser usados com classes, mesmo se a classe não sobrecarregar a eles. No entanto, o comportamento padrão é executar uma verificação de igualdade de referência. Em uma classe, se você sobrecarregar o Equals método, você deverá sobrecarregar o == e != operadores, mas não é necessária.
O exemplo a seguir mostra como implementar a igualdade do valor em uma estrutura (tipo de valor):
struct TwoDPoint : IEquatable<TwoDPoint>
{
// Read/write auto-implemented properties.
public int X { get; private set; }
public int Y { get; private set; }
public TwoDPoint(int x, int y)
: this()
{
X = x;
Y = x;
}
public override bool Equals(object obj)
{
if (obj is TwoDPoint)
{
return this.Equals((TwoDPoint)obj);
}
return false;
}
public bool Equals(TwoDPoint p)
{
return (X == p.X) && (Y == p.Y);
}
public override int GetHashCode()
{
return X ^ Y;
}
public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs)
{
return !(lhs.Equals(rhs));
}
}
class Program
{
static void Main(string[] args)
{
TwoDPoint pointA = new TwoDPoint(3, 4);
TwoDPoint pointB = new TwoDPoint(3, 4);
int i = 5;
// Compare using virtual Equals, static Equals, and == and != operators.
// True:
Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
// True:
Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
// True:
Console.WriteLine("Object.Equals(pointA, pointB) = {0}", Object.Equals(pointA, pointB));
// False:
Console.WriteLine("pointA.Equals(null) = {0}", pointA.Equals(null));
// False:
Console.WriteLine("(pointA == null) = {0}", pointA == null);
// True:
Console.WriteLine("(pointA != null) = {0}", pointA != null);
// False:
Console.WriteLine("pointA.Equals(i) = {0}", pointA.Equals(i));
// CS0019:
// Console.WriteLine("pointA == i = {0}", pointA == i);
// Compare unboxed to boxed.
System.Collections.ArrayList list = new System.Collections.ArrayList();
list.Add(new TwoDPoint(3, 4));
// True:
Console.WriteLine("pointE.Equals(list[0]): {0}", pointA.Equals(list[0]));
// Compare nullable to nullable and to non-nullable.
TwoDPoint? pointC = null;
TwoDPoint? pointD = null;
// False:
Console.WriteLine("pointA == (pointC = null) = {0}", pointA == pointC);
// True:
Console.WriteLine("pointC == pointD = {0}", pointC == pointD);
TwoDPoint temp = new TwoDPoint(3, 4);
pointC = temp;
// True:
Console.WriteLine("pointA == (pointC = 3,4) = {0}", pointA == pointC);
pointD = temp;
// True:
Console.WriteLine("pointD == (pointC = 3,4) = {0}", pointD == pointC);
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
}
/* Output:
pointA.Equals(pointB) = True
pointA == pointB = True
Object.Equals(pointA, pointB) = True
pointA.Equals(null) = False
(pointA == null) = False
(pointA != null) = True
pointA.Equals(i) = False
pointE.Equals(list[0]): True
pointA == (pointC = null) = False
pointC == pointD = True
pointA == (pointC = 3,4) = True
pointD == (pointC = 3,4) = True
*/
}
Para estruturas, a implementação padrão do Object.Equals(Object) (que é a versão substituída na System.ValueType) executa uma verificação de igualdade de valor usando a reflexão para comparar os valores de cada campo de tipo. Quando um implementador substitui o virtual Equals método em um stuct, o objetivo é fornecer um meio mais eficiente de realizar a verificação de igualdade do valor e, opcionalmente, para basear a comparação de alguns subconjuntos de campo do struct ou propriedades.
O = = e ! = operadores não podem operar em uma struct, a menos que explicitamente struct overloads-los.
Consulte também
Referência
Diretrizes para a implementação de Equals e o operador de igualdade (= =)