2009-09-14 11 views
7

Od MSDNIComparable i equals()

typy, które implementują IComparable musi zastąpić Equals.Types że przesłanianie równa się musi także przesłonić GetHashCode; w przeciwnym razie Hashtable może nie działać poprawnie.

Nie bardzo to rozumiem. Czy ktoś może wyjaśnić.

+0

Aby uzyskać więcej relacji między równymi i GetHashCode: http: // stackoverflow.com/questions/371328/why-is-it-it-important-to-override-gethashcode-when-equals-method-is-overriden-in- –

+0

Należy również pamiętać, że o ile konieczne jest, aby umowy IComparable, Equals i GetHashCode() są przestrzegane w każdym przypadku, nie jest koniecznie wymagane jawne wdrożenie wszystkich trzech, o ile osiąga się spójne wyniki. Przede wszystkim oznacza to, że dwa obiekty z tym samym kodem mieszającym są równe i zwracają 0 w funkcji CompareTo(). – rushinge

+0

@rushinge Myślę, że instrukcja jest błędna: "Oznacza to przede wszystkim, że dwa obiekty z tym samym kodem skrótu są równe i zwracają 0 w funkcji CompareTo()." . Jeśli obiekty są równe, zaleca się, aby funkcja GetHashCode() zwróciła tę samą wartość, a funkcja CompareTo() zwróciła 0. Można łatwo tworzyć różne/nie równe instancje zwracające ten sam kod skrótu :) –

Odpowiedz

11

IComparable to interfejs, który definiuje, że dwa wystąpienia klasy implementacji mogą być postrzegane jako większe, mniejsze lub równe sobie. Ponieważ zdefiniowałeś równość w metodach tego interfejsu, musisz również zastąpić metodę Równości (i operator równości), aby zapewnić, że wyniki z dwóch są spójne.

public class EqualityTest : IComparable<EqualityTest> 
{ 
     public int Value { get; set; } 

     public int CompareTo(EqualityTest other) 
     { 
      return this.Value.CompareTo(other.Value); 
     } 
} 

W powyższym przykładzie zaimplementowałem IComparable, ale nie zastąpiłem Equals. Jeśli wywołasz funkcję CompareTo z dwoma oddzielnymi instancjami klasy, które mają tę samą wartość, powiedzą, że są równe. Jeśli wywołasz Equals z tymi samymi dwoma instancjami, będzie to oznaczało, że są one równe, ponieważ będą sprawdzać, czy są to te same obiekty (domyślna implementacja równań).

Dwie równe przedmioty powinny powrócić ten sam kod skrótu (które są wykorzystywane do szybkiego znalezienia przedmioty używane jako klucze w tabelach hash) więc jeśli przesłonić Równa się to należy również zastąpić GetHashCode()


Jako przykład właśnie utworzona następujące klasy w moim IDE:

public class EqualityTest 
{ 
    public string A { get; set; } 
    public string B { get; set; } 
} 

i prowadził „równości” Generate funkcji ReSharper pomocne mówiąc, że chciałem A i B wpływa na równości. Jest to kod stworzył:

public bool Equals(EqualityTest other) 
    { 
     if (ReferenceEquals(null, other)) 
     { 
      return false; 
     } 

     if (ReferenceEquals(this, other)) 
     { 
      return true; 
     } 

     return Equals(other.A, A) && Equals(other.B, B); 
    } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) 
     { 
      return false; 
     } 

     if (ReferenceEquals(this, obj)) 
     { 
      return true; 
     } 

     if (obj.GetType() != typeof(EqualityTest)) 
     { 
      return false; 
     } 

     return Equals((EqualityTest)obj); 
    } 

    public override int GetHashCode() 
    { 
     unchecked 
     { 
      return ((A != null ? A.GetHashCode() : 0)*397)^(B != null ? B.GetHashCode() : 0); 
     } 
    } 

    public static bool operator ==(EqualityTest left, EqualityTest right) 
    { 
     return Equals(left, right); 
    } 

    public static bool operator !=(EqualityTest left, EqualityTest right) 
    { 
     return !Equals(left, right); 
    } 

Więc jeśli są nadrzędne Równa się to należy również określić wszystkie powyższe w celu zapewnienia spójności, jeśli wdrażają IComparable to samo.

+0

Moim zdaniem, jeśli istnieje możliwość, że typ ogólnie jest wyraźnym porządkiem w rankingu, ale mogą istnieć grupy wartości, które nie są sobie równe, ale które nie mają naturalnego rankingu względem siebie, byłoby całkowicie właściwe, aby 'CompareTo()' powrócił zero, ale dla 'Equals()' zwraca false (np. Założę się, że 'Decimal' powinien zachowywać się w ten sposób podczas porównywania' 1.0m' i '1.00m'). Moim zdaniem "Equals" powinno definiować relatywnie ścisłą relację równości, nawet w przypadkach, gdy '==' lub 'CompareTo' może definiować przegraną. – supercat

0

Istnieją dwa sposoby, że obiekty w kodzie można porównać: Equals i GetHashCode

dla obiektu należy prawidłowo porównaniu we wszystkich sytuacjach, kiedy zastąpić metodę Equals (używany przez kilka porównań), należy również zastąpić GetHashCode (używane w dalszej części).

Jeśli zastąpisz jeden, ale nie drugi, w zależności od kodu, możesz uzyskać nieoczekiwane wyniki.

1

Funkcja IComparable jest używana do porównywania dwóch obiektów - jeśli są one uważane za równe, to wartość Porównaj zwróci 0. To byłoby bardzo nieoczekiwane, jeśli IComparable.Compare zwrócił zero dla dwóch obiektów, a mimo to obj1.Equals (obj2) zwrócił wartość false, ponieważ spowodowałoby to implikują dwa różne znaczenia równości dla obiektów.

Gdy klasa zastępuje wartości równe, powinna również nadpisać kod GetHashCode, ponieważ dwa równe obiekty powinny mieszać się do tej samej wartości, a wartość ta powinna być oparta na polach/właściwościach użytych w implementacji równości.

+0

Jeśli IComparable.Compare zwraca zero, czy to naprawdę oznacza, że ​​elementy powinny być traktowane jako równe, czy tylko, że nie ma wyraźnego uporządkowanego związku? Załóżmy, że mamy niezmienną klasę ScheduleEvent, która zawiera pola EventTime typu DateTime i EventAction typu MethodInvoker. Obiekty ScheduleEvent miałyby naturalną kolejność opartą na EventTime, ale chciałbym, aby obiekty ScheduleEvent z tym samym EventTime, ale różni delegaci powinni zwrócić 0 z CompareTo, ale False from Equals. Wydawałoby się to czystsze niż jakiekolwiek inne zachowanie. – supercat

Powiązane problemy