A maioria implementação Equals robusta para classes personalizadas para valor igualdade em C #
Pergunta
Say Eu tenho uma classe Point2, e eu quero implementar o seguinte Igual a:
public override bool Equals ( object obj )
public bool Equals ( Point2 obj )
Esta é a partir do Effective C # 3 livro:
public override bool Equals ( object obj )
{
// STEP 1: Check for null
if ( obj == null )
{
return false;
}
// STEP 3: equivalent data types
if ( this.GetType ( ) != obj.GetType ( ) )
{
return false;
}
return Equals ( ( Point2 ) obj );
}
public bool Equals ( Point2 obj )
{
// STEP 1: Check for null if nullable (e.g., a reference type)
if ( obj == null )
{
return false;
}
// STEP 2: Check for ReferenceEquals if this is a reference type
if ( ReferenceEquals ( this, obj ) )
{
return true;
}
// STEP 4: Possibly check for equivalent hash codes
if ( this.GetHashCode ( ) != obj.GetHashCode ( ) )
{
return false;
}
// STEP 5: Check base.Equals if base overrides Equals()
System.Diagnostics.Debug.Assert (
base.GetType ( ) != typeof ( object ) );
if ( !base.Equals ( obj ) )
{
return false;
}
// STEP 6: Compare identifying fields for equality.
return ( ( this.X.Equals ( obj.X ) ) && ( this.Y.Equals ( obj.Y ) ) );
}
Isso é um exagero?
Solução
Apoiar a igualdade com uma hierarquia de herança é complicado. Você precisa trabalhar para fora exatamente o que você quer dizer. Você realmente precisa de herança aqui? Se não - se Point2 deriva directamente de System.Object, e você pode fazê-lo selado, a vida se torna um pouco mais fácil. Nesse caso, eu usaria:
public override bool Equals (object obj)
{
return Equals(obj as Point2);
}
public bool Equals (Point2 obj)
{
// STEP 1: Check for null if nullable (e.g., a reference type)
// Note use of ReferenceEquals in case you overload ==.
if (object.ReferenceEquals(obj, null))
{
return false;
}
// STEP 2: Check for ReferenceEquals if this is a reference type
// Skip this or not? With only two fields to check, it's probably
// not worth it. If the later checks are costly, it could be.
if (object.ReferenceEquals( this, obj))
{
return true;
}
// STEP 4: Possibly check for equivalent hash codes
// Skipped in this case: would be *less* efficient
// STEP 5: Check base.Equals if base overrides Equals()
// Skipped in this case
// STEP 6: Compare identifying fields for equality.
// In this case I'm using == instead of Equals for brevity
// - assuming X and Y are of a type which overloads ==.
return this.X == obj.X && this.Y == obj.Y;
}
Outras dicas
Não é verdade - você está respondendo por praticamente todas as possibilidades. Se este código é para qualquer coisa diferente de um aplicativo do zero você deve considerar os benefícios desta abordagem, porque erros lógicos devido ao comportamento estranho objeto igualdade são dolorosas para depuração.
Parece-me que exatamente o que você deseja. Esse bloco inteiro se resume a:
"se é exatamente o mesmo exemplo, retornar true. Se eles são instâncias separadas com os mesmos valores X e Y, retornar true. Todos os outros casos (null, tipo diferente, diferentes x / valores y) return false".
Sua certamente mais código do eu quiser escrever para um método equals. Há uma série de verificações redundantes, como a verificação de ReferenceEquals e hashcodes (Eu sei que estas verificações são redundantes porque as últimas linhas na sua função verifica a igualdade estrutural). Foco no código que é simples e legível.
public bool Equals(object o)
{
Point2 p = o as Point2;
if (o != null)
return this.X == o.X && this.Y == o.Y;
else
return false;
}
Uma vez que o seu método equals usa igualdade estrutural, certifique-se de substituir GetHashCode com uma aplicação baseada em seus campos.