Qual è la "migliore" implementazione canonica di Equals() per i tipi di riferimento?
Domanda
L'implementazione di Equals() per i tipi di riferimento è più difficile di quanto sembri.La mia attuale implementazione canonica è questa:
public bool Equals( MyClass obj )
{
// If both refer to the same reference they are equal.
if( ReferenceEquals( obj, this ) )
return true;
// If the other object is null they are not equal because in C# this cannot be null.
if( ReferenceEquals( obj, null ) )
return false;
// Compare data to evaluate equality
return _data.Equals( obj._data );
}
public override bool Equals( object obj )
{
// If both refer to the same reference they are equal.
if( ReferenceEquals( obj, this ) )
return true;
// If the other object is null or is of a different types the objects are not equal.
if( ReferenceEquals( obj, null ) || obj.GetType() != GetType() )
return false;
// Use type-safe equality comparison
return Equals( (MyClass)obj );
}
public override int GetHashCode()
{
// Use data's hash code as our hashcode
return _data.GetHashCode();
}
Penso che questo copra tutti i casi limite (eredità e simili), ma potrei sbagliarmi.Che cosa ne pensate?
Soluzione
Tempo fa ho scritto una guida abbastanza completa sull'argomento.Per cominciare, le tue implementazioni uguali dovrebbero essere condivise (ad es.il sovraccarico che prende un oggetto dovrebbe passare a quello che prende un oggetto fortemente tipizzato).Inoltre devi considerare cose come il tuo oggetto che dovrebbe essere immutabile a causa della necessità di sovrascrivere GetHashCode.Maggiori informazioni qui:
http://gregbeech.com/blog/implementing-object-equality-in-dotnet
Altri suggerimenti
Meglio sperare che this._data non sia null se è anche un tipo di riferimento.
public bool Equals( MyClass obj )
{
if (obj == null) {
return false;
}
else {
return (this._data != null && this._data.Equals( obj._data ))
|| obj._data == null;
}
}
public override bool Equals( object obj )
{
if (obj == null || !(obj is MyClass)) {
return false;
}
else {
return this.Equals( (MyClass)obj );
}
}
public override int GetHashCode() {
return this._data == null ? 0 : this._data.GetHashCode();
}
Per quanto riguarda l'ereditarietà, penso che dovresti lasciare che il paradigma OO faccia la sua magia.
Nello specifico, il GetType()
check dovrebbe essere rimosso, potrebbe interrompere il polimorfismo su tutta la linea.
Sono d'accordo con chakrit, oggetti di tipo diverso dovrebbero poter essere semanticamente uguali se hanno gli stessi dati o ID.
Personalmente utilizzo quanto segue:
public override bool Equals(object obj)
{
var other = obj as MyClass;
if (other == null) return false;
return this.data.Equals(other.data);
}
Dipende se stai scrivendo un tipo valore o un tipo riferimento.Per un tipo di valore ordinabile, consiglio questo:Uno snippet di codice per Visual Studio 2005 che implementa un tipo di valore di base aderente alle linee guida per la progettazione del framework