2009-10-29 21 views
22

Mam dwie kolekcje własnych obiektów typu referencyjnego, dla których napisałem własną metodę IEquatable.Equals i chcę mieć możliwość korzystania z metod LINQ na nich.Dlaczego nie jest .Except (LINQ) prawidłowo porównując rzeczy? (używając IEquatable)

Więc

List<CandyType> candy = dataSource.GetListOfCandy(); 
List<CandyType> lollyPops = dataSource.GetListOfLollyPops(); 
var candyOtherThanLollyPops = candy.Except(lollyPops); 

Według dokumentacji .Except, nie przechodząc IEqualityComparer powinno skutkować EqualityComparer.Default używany do porównania obiektów. Dokumentacja dla domyślnego porównywalnika jest następująca:

"Właściwość Default sprawdza, czy typ T implementuje ogólny interfejs System.IEquatable, a jeśli tak, zwraca EqualityComparer, który używa tej implementacji. W przeciwnym razie zwraca EqualityComparer, który używa przesłonięć Object.Equals i Object.GetHashCode dostarczone przez T. "

Tak więc, ponieważ implementuję IEquatable dla mojego obiektu, powinien on używać tego i pracować. Ale tak nie jest. To nie działa, dopóki nie nadpisuję GetHashCode. W rzeczywistości, jeśli ustawię punkt przerwania, moja metoda IEquatable.Equals nigdy nie zostanie wykonana. To sprawia, że ​​myślę, że dzieje się z planem B zgodnie z jego dokumentacją. Rozumiem, że nadpisywanie kodu GetHashCode i tak jest dobrym pomysłem, i mogę to zrobić działa, ale jestem zdenerwowany, że zachowuje się w sposób niezgodny z własną dokumentacją.

Dlaczego nie robi to, co powiedział? Dziękuję Ci.

+0

Spróbuj użyć EqualityComparer.Default bezpośrednio i sprawdzić, czy niezgodność jest w tej realizacji, lub metodą LINQ na początek. Następnie otwórz Reflector i sprawdź źródło i dodaj komentarz do dokumentów MSDN? – MichaelGG

+0

Jako węzeł boczny, rzeczy, które nie zachowują się jak udokumentowane, są uważane za błędy, więc zachęcam do przesłania ich jako Microsoft Connect. Do tej pory miałem mniejsze błędy w dokumentacji zgłoszone za pośrednictwem tego kanału naprawione w przeszłości. –

Odpowiedz

17

Po przeprowadzeniu dochodzenia okazało się, że sprawy nie są tak złe, jak myślałem. Zasadniczo, gdy wszystko jest poprawnie zaimplementowane (GetHashCode itp.), Dokumentacja jest poprawna, a zachowanie jest poprawne.Ale jeśli spróbujesz zrobić coś takiego jak IEquatable, wtedy twoja metoda Equals nigdy nie zostanie wywołana (wydaje się, że nie jest poprawnie implementowany kod GetHashCode). Tak więc, chociaż dokumentacja jest zła technicznie, to tylko w złym stanie sytuacji, której nigdy byś nie chciał zrobić (jeśli to badanie nauczyło mnie czegokolwiek, to jest to, że IEquatable jest częścią całego zestawu metod, które powinieneś implementować atomowo (przez konwencję, nie przez regułę, niestety)). Dobre źródła na ten temat to:

Is there a complete IEquatable implementation reference?

http://msdn.microsoft.com/en-us/library/ms131190.aspx

http://blogs.msdn.com/irenak/archive/2006/07/18/669586.aspx

+0

Instrukcja Except używa metody GetHashCode do sprawdzenia, czy dwa obiekty powinny być traktowane tak samo, dlatego nie powiedzie się, jeśli nie w pełni wdrożysz IEquatable –

0

Ryzyko zgadywania, czy te różne klasy? Domyślnie IEquatable działa tylko z tą samą klasą. A więc może się cofnąć do metody ObjectEqual.

+0

Czym są różne klasy? Zmieniłem to pytanie, aby było bardziej jednoznaczne na temat typów obiektów w kolekcjach. Mam nadzieję, że to pomaga. –

10

Interfejs IEqualityComparer<T> ma te dwie metody:

bool Equals(T x, T y); 
int GetHashCode(T obj); 

Dobrym implementacja tego interfejsu będzie zatem zarówno wdrożenia. Metoda rozszerzenia Linq Oprócz tego, że wymaga użycia kodu skrótu, aby użyć słownika lub wewnętrznego wyszukiwania, aby dowiedzieć się, które obiekty pominąć, a tym samym wymaga odpowiedniej implementacji GetHashCode.

Niestety, podczas korzystania EqualityComparer<T>.Default, że klasa nie zapewnia dobrego wykonania GetHashCode przez siebie, i opiera się na danego przedmiotu, typu T, aby zapewnić, że część, gdy wykryje, że obiekt implementuje IEquatable<T>.

Problem polega na tym, że IEquatable<T> w rzeczywistości nie deklarują GetHashCode tak jest dużo łatwiej zapomnieć wdrożyć tę metodę prawidłowo, skontrastowane z metodą Equals że nie deklarują.

więc masz dwie możliwości:

  • Zapewnić odpowiednią IEqualityComparer<T> implementację, która implementuje zarówno Equals i GetHashCode
  • Upewnij się, że oprócz realizacji IEquatable<T> na swoim obiekcie, wdrożenie właściwego GetHashCode także
+1

Aby było jasne, w ogóle nie zaimplementowałem IEqualityComparer - tylko IEquatable . chodzi o wybór 1, moje zrozumienie jest, że jeśli tworzę własne porównywarka który implementuje IEqualityComparer , będę wtedy musiał przekazać tę porównywarka do każdej metody LINQ kiedykolwiek wykorzystać, całego kodu. To nie jest atrakcyjne. Myślę, że wszystko, co powiedziałeś jest prawdą, ale nie odnosi się dlaczego coś nie działa jak dokumentacja mówi (konkretnie moja IEquatable metoda .equals nigdy nie jest wywoływana). –

Powiązane problemy