Chcę zrozumieć scenariusze, w których IEqualityComparer<T>
i IEquatable<T>
należy je stosować. Dokumentacja MSDN dla obu wygląda bardzo podobnie.
151
Chcę zrozumieć scenariusze, w których IEqualityComparer<T>
i IEquatable<T>
należy je stosować. Dokumentacja MSDN dla obu wygląda bardzo podobnie.
EqualityComparer<T>
zamiast implementowania interfejsu ", ponieważEqualityComparer<T>
testuje równość przy użyciuIEquatable<T>
T
wdrożeniaIEquatable<T>
. Czy w przeciwnym razie kolekcjaList<T>
miałaby jakiś subtelny błąd?Odpowiedzi:
IEqualityComparer<T>
jest interfejsem dla obiektu, który przeprowadza porównanie dwóch obiektów tego typuT
.IEquatable<T>
dotyczy obiektu typu,T
aby mógł porównać się z innym obiektem tego samego typu.źródło
IEqualityComparer<T>
to interfejs dla obiektu (który jest zwykle lekką klasą różną odT
), który zapewnia funkcje porównawcze, które działająT
Decydując się na użycie
IEquatable<T>
lubIEqualityComparer<T>
, można zapytać:Jeśli istnieje tylko jeden sposób testowania dwóch wystąpień
T
równości lub jeśli preferowana jest jedna z kilku metod,IEquatable<T>
byłby to właściwy wybór: ten interfejs powinien być implementowany tylkoT
samodzielnie, tak aby jedna instancjaT
miała wewnętrzną wiedzę o jak porównać się z innym wystąpieniemT
.Z drugiej strony, jeśli istnieje kilka równie rozsądnych metod porównywania dwóch
T
dla równości,IEqualityComparer<T>
wydaje się bardziej odpowiedni: Ten interfejs nie ma być implementowanyT
samodzielnie, ale przez inne „zewnętrzne” klasy. Dlatego podczas testowania dwóch instancjiT
równości, ponieważT
nie ma wewnętrznego zrozumienia równości, będziesz musiał dokonać wyraźnego wyboruIEqualityComparer<T>
instancji, która przeprowadza test zgodnie z określonymi wymaganiami.Przykład:
Rozważmy te dwa typy (które mają mieć semantykę wartości ):
Dlaczego tylko jeden z tych typów miałby dziedziczyć
IEquatable<>
, a drugi nie?W teorii istnieje tylko jeden rozsądny sposób porównania dwóch wystąpień każdego typu: są one równe, jeśli właściwości
X
iY
w obu przypadkach są równe. Zgodnie z tym myśleniem oba typy powinny wdrożyćIEquatable<>
, ponieważ nie wydaje się prawdopodobne, że istnieją inne sensowne sposoby przeprowadzania testu równości.Problem polega na tym, że porównywanie liczb zmiennoprzecinkowych pod kątem równości może nie działać zgodnie z oczekiwaniami z powodu drobnych błędów zaokrągleń . Istnieją różne metody porównywania liczb zmiennoprzecinkowych pod kątem niemal równości , z których każda ma określone zalety i kompromisy, i możesz chcieć samodzielnie wybrać, która metoda jest odpowiednia.
Zauważ, że strona, do której odsyłam (powyżej), wyraźnie stwierdza, że ten test bliskiej równości ma pewne słabości. Ponieważ jest to
IEqualityComparer<T>
implementacja, możesz ją po prostu zamienić, jeśli nie jest wystarczająco dobra do twoich celów.źródło
IEqualityComparer<T>
implementacja musi implementować relację równoważności, co oznacza, że we wszystkich przypadkach, gdy dwa obiekty są porównywane jako równe trzeciemu, muszą być porównywane równo ze sobą. Każda klasa, która wdrażaIEquatable<T>
lubIEqualityComparer<T>
w sposób sprzeczny z powyższym, jest zepsuta.IEqualityComparer<String>
które uważa „hello” za równe zarówno „Hello”, jak i „hElLo”, musi traktować „Hello” i „hElLo” jako równe sobie, ale w przypadku większości metod porównawczych nie byłoby to problemem.GetHashCode
powinno umożliwić szybkie określenie, czy dwie wartości się różnią. Reguły są z grubsza następujące: (1)GetHashCode
musi zawsze generować ten sam kod skrótu dla tej samej wartości. (2)GetHashCode
powinno być szybkie (szybsze niżEquals
). (3)GetHashCode
nie musi być precyzyjne (nie tak dokładne jakEquals
). Oznacza to, że może generować ten sam kod skrótu dla różnych wartości. Im dokładniej możesz to zrobić, tym lepiej, ale prawdopodobnie ważniejsze jest, aby to robić szybko.Masz już podstawową definicję tego, czym one są . Krótko mówiąc, jeśli zaimplementujesz
IEquatable<T>
w klasieT
,Equals
metoda na obiekcie typuT
informuje, czy sam obiekt (testowany pod kątem równości) jest równy innemu wystąpieniu tego samego typuT
. NatomiastIEqualityComparer<T>
służy do testowania równości dowolnych dwóch wystąpieńT
, zwykle poza zakresem wystąpieńT
.Co do tego, do czego służą, może być początkowo mylące. Z definicji powinno być jasne, że stąd
IEquatable<T>
(zdefiniowana wT
samej klasie ) powinna być de facto standardem reprezentacji unikalności jej obiektów / instancji.HashSet<T>
,Dictionary<T, U>
(GetHashCode
branie pod uwagę również jest nadpisywane),Contains
naList<T>
etc korzystają z tego. WdrożenieIEqualityComparer<T>
naT
nie pomaga powyższe ogólne przypadki. W związku z tym implementacjaIEquatable<T>
w innej klasie niżT
. To:rzadko ma sens.
Z drugiej strony
tak należy to zrobić.
IEqualityComparer<T>
może być przydatne, gdy potrzebujesz niestandardowej weryfikacji równości, ale nie jako ogólna zasada. Na przykład, wPerson
pewnym momencie w klasie może być konieczne przetestowanie równości dwóch osób na podstawie ich wieku. W takim przypadku możesz:Aby je przetestować, spróbuj
Podobnie
IEqualityComparer<T>
naT
nie ma sensu.To prawda, że to działa, ale nie wygląda dobrze dla oczu i podważa logikę.
Zwykle to, czego potrzebujesz
IEquatable<T>
. Idealnie byłoby, gdybyś miał tylko jeden,IEquatable<T>
podczas gdy wieleIEqualityComparer<T>
jest możliwych na podstawie różnych kryteriów.IEqualityComparer<T>
IIEquatable<T>
są dokładnie analogiczne doComparer<T>
iIComparable<T>
które są wykorzystywane do celów porównawczych zamiast zrównując; dobry wątek , w którym napisałem tę samą odpowiedź :)źródło
public int GetHashCode(Person obj)
powinien wrócićobj.GetHashCode()
obj.Age.GetHashCode
. będzie edytować.person.GetHashCode
nigdzie bezpośrednio nie zadzwoniliśmy … dlaczego miałbyś to zmienić? - Nadpisujemy, bo chodzi oIEqualityComparer
to, żeby mieć inną implementację porównania według naszych reguł - reguł, które naprawdę dobrze znamy.Age
właściwości, więc dzwonięAge.GetHashCode
. Wiek jest typuint
, ale niezależnie od typu jest to kontrakt z kodem skrótu w .NETdifferent objects can have equal hash but equal objects cant ever have different hashes
. To kontrakt, w który ślepo wierzymy. Na pewno nasze niestandardowe porównywarki również powinny przestrzegać tej zasady. Jeśli kiedykolwiek dzwonieniesomeObject.GetHashCode
psuje rzeczy, to jest to problem z implementatorami typusomeObject
, to nie jest nasz problem.IEqualityComparer jest używany, gdy równość dwóch obiektów jest implementowana zewnętrznie, np. Jeśli chcesz zdefiniować funkcję porównującą dla dwóch typów, dla których nie masz źródła, lub w przypadkach, w których równość między dwiema rzeczami ma sens tylko w pewnym ograniczonym kontekście.
IEquatable służy do zaimplementowania samego obiektu (tego, który jest porównywany pod kątem równości).
źródło
Jeden porównuje dwa
T
s. Drugi może porównać się do innychT
. Zwykle wystarczy użyć tylko jednego na raz, a nie obu.źródło