2009-03-04 9 views
6

Se eseguo l'override di Equals e GetHashCode, come faccio a decidere quali campi confrontare? E cosa succederà se avrò due oggetti con due campi ciascuno, ma Equals controlla solo un campo?Overriding Equals() ma non controlla tutti i campi - cosa accadrà?

In altre parole, diciamo che ho questa classe:

class EqualsTestClass 
{ 
    public string MyDescription { get; set; } 
    public int MyId { get; set; } 

    public override bool Equals(object obj) 
    { 
     EqualsTestClass eq = obj as EqualsTestClass; 
     if(eq == null) { 
      return false; 
     } else { 
      return MyId.Equals(eq.MyId); 
     } 
    } 

    public override int GetHashCode() 
    { 
     int hashcode = 23; 
     return (hashcode * 17) + MyId.GetHashCode(); 
    } 
} 

considero due oggetti uguali se hanno lo stesso myid. Quindi se l'Id è uguale ma la descrizione è diversa, sono comunque considerate uguali.

Mi chiedo quali sono le insidie ​​di questo approccio? Naturalmente, un costrutto come questo si comporterà come previsto:

 List<EqualsTestClass> test = new List<EqualsTestClass>(); 

     EqualsTestClass eq1 = new EqualsTestClass(); 
     eq1.MyId = 1; 
     eq1.MyDescription = "Des1"; 

     EqualsTestClass eq2 = new EqualsTestClass(); 
     eq2.MyId = 1; 
     eq2.MyDescription = "Des2"; 

     test.Add(eq1); 
     if (!test.Contains(eq2)) 
     { 
      // Will not be executed, as test.Contains is true 
      test.Add(eq2); 
     } 

Come eq2 è valore pari a EQ1, non sarà aggiunto. Ma questo è il codice che controllo, ma mi chiedo se nel framework ci sia del codice che potrebbe causare problemi imprevisti?

Quindi, dovrei sempre aggiungere tutti i campi pubblici nel mio confronto Equals(), o quali sono le linee guida per evitare una brutta sorpresa a causa di un pessimo framework Mojo che era completamente inaspettato?

risposta

11

Il motivo per ignorare Equals() è che si definisce, cosa significa che due istanze sono uguali. In alcuni casi ciò significa che tutti i campi devono essere uguali, ma non è necessario. Tu decidi.

Per ulteriori informazioni, vedere documentation e questo post.

+0

concordato, come il PO, ho overidden Equals() per controllare solo un ID primario, e questo è tutto, è totalmente la chiamata dei programmatori. –

1

Non credo che sia necessario preoccuparsi del Framework in questo caso. Se la Class Designer considera due istanze di quella classe uguali se condividono lo stesso MyId, quindi, è sufficiente testare MyId nei metodi overriden Equals() e GetHashCode().

1

È necessario solo verificare i campi che sono necessari per la corrispondenza, se tutto ciò che deve corrispondere è l'ID quindi andare con quello.

1

Una domanda: se eseguo l'override di Equals e GetHashCode, come faccio a decidere quali campi confrontare?

Dipende da ciò che si sta tentando di realizzare. Se stai cercando di vedere se gli oggetti sono esattamente gli stessi dovresti confrontarli tutti. Se hai una "chiave" e vuoi solo sapere se sono lo stesso "oggetto", anche se altri dati sono diversi, basta controllare i valori della "chiave".

E che succede se ho due oggetti con due campi ciascuno, ma Uguale controlla solo un campo?

allora si avrà un metodo di uguaglianza che controlla solo per vedere se la 'chiave' è lo stesso, e potrebbe potenzialmente avere più oggetti uguali '' che hanno varianze interne.

1

Altri hanno detto che questo è perfettamente valido e previsto, ed è esattamente come Equal dovrebbe funzionare. Quindi non c'è alcun problema con esso come classe.

Sarei molto leggermente diffidente su questo come API. Perdonami se non è ciò che era inteso: in questo caso è solo una nota di cautela per gli altri.

Il potenziale problema è che gli utenti dell'API si aspetteranno naturalmente che gli stessi oggetti siano "uguali". Questo non fa parte del contratto di uguaglianza, ma fa parte del significato comune del termine. La classe assomiglia un po 'a una tupla binaria, ma non è una, quindi dovrebbe essere per ragioni sensate.

Un esempio di tale ragionevole motivo è che un campo è un "dettaglio di implementazione visibile", come il fattore di carico massimo in un contenitore basato su hashtable. Un esempio di una ragione rischiosa (sebbene allettante) è "perché ho aggiunto la descrizione in seguito e non ho voluto modificare il metodo Equals nel caso in cui abbia rotto qualcosa".

Quindi è completamente valido fare qualcosa di leggermente controintuitivo, specialmente se si documenta chiaramente che il comportamento potrebbe essere sorprendente. Tali metodi devono essere supportati, perché il loro bando sarebbe pazzesco nei casi in cui un campo è ovviamente irrilevante. Ma dovrebbe essere chiaro perché ha senso creare due coppie di descrizioni ID con lo stesso ID e descrizioni diverse, ma non ha senso aggiungerle entrambe a un contenitore (come HashSet) che usa Equals/HashCode per evitare duplicati inserimenti.

0

L'esempio sta verificando se un elenco (di EqualsTestClass) contiene un oggetto dello stesso tipo con gli stessi valori di proprietà. Un altro modo per eseguire questa attività senza eseguire l'override degli equals (e la comprensione tradizionale di equals) è utilizzare un comparatore personalizzato. Sarebbe simile a questa (in VB):

Public Class EqualsTestComparer 
Implements IEqualityComparer(Of EqualsTestClass) 
Public Function Equals1(ByVal x As EqualsTestClass, ByVal y As EqualsTestClass) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).Equals 
    If x.MyId = y.MyId and x.MyDescription = y.MyDescription Then 
     Return True 
    Else 
     Return False 
    End If 
End Function 

Public Function GetHashCode1(ByVal obj As EqualsTestClass) As Integer Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).GetHashCode 
    Return obj.ToString.ToLower.GetHashCode 
End Function  
End Class 

Poi nella vostra routine è sufficiente utilizzare l'operatore di confronto personalizzato:

If Not test.Contains(eq2, New EqualsTestComparer) Then 
    //Do Stuff 
End if 
Problemi correlati