List<T> i Contains()

0

Siemka
Piszę sobie i piszę, i naraz patrzę nie działa:

// productsInDatabase - List<Product>
// product - Product
if (productsInDatabase.Contains(product) == false)
{
    productInDatabase.Add(product);
}

prosty kod. Ale problem jest z contains(). mam wpisany już produkt do tej listy, po czym wywołuję funcję z tym kodem i product jako parametrem. Ten product już powinien być w liście, referencja nie powinna się nigdzie zmienić, nie był tworzony nowy product tylko edytowany stary. Logika podpowiada - containst() powinno zwrócić true. I tak się działo u kumpla, wszystko chodziło poprawnie. U mnie jednak contains() zwracało false po edycji produktu.
Rada się znalazła oczywiście, implementacja IEquatable<Product> i wszystko chodzi jak należy ale... czemu kod, który u mnie zwraca false w tej samej sytuacji na innym kompie zwraca true? Już nieważne, że referencja gdzieś się mogła zmienić i u mnie leciało true. Ten sam kod, w tych samych warunkach powinien się zachowywać tak samo, nie?
Kod na .NET 2.0
Ciekawe jest to, zastanawia mnie i już :) może ktoś się z tym spotkał?

0

Żeby takie porównanie było jednoznaczne trzeba najwidoczniej zaimplementować interfejs IEquatable<T>. Wynika to z faktu że List<T>.Contains(T) do porównania wykorzystuje EqualityComparer(T).Default gdzie ta właściwość sprawdza czy typ T implementuje IEquatable<T> zwracając stosowny comparer. Jeżeli odnotowany zostaje brak implementacji, porównywanie opierane jest na przeciążeniach Object.Equals i Object.GetHashCode gdzie w dokumentacji tego drugiego można wyczytać.

The default implementation of the GetHashCode method does not guarantee unique return values for different objects. Furthermore, the .NET Framework does not guarantee the default implementation of the GetHashCode method, and the value it returns will be the same between different versions of the .NET Framework. Consequently, the default implementation of this method must not be used as a unique object identifier for hashing purposes.

I wszystko staje się jasne :) Różny komputer/wersja .NET i już może wyjść coś innego.</quote>

0

No dobra, ale błąd występuje u mnie. Więc: nie było przeładowania IEquotable<T>, bierze więc GetHashCode(). Ale zgodnie z logiką - ta funkcja, na moim kompie, na moim .NETcie powinna za każdym razem zwracać tę samą wartość dla tego samego obiektu. Czyli:
product.GetHashCode() == product.GetHashCode();
inaczej jest to bez sensu.
W liście jest referencja do product, w parametrze też jest referencja do product. A jednak .NET nie odnajduje jej i dodaje ten sam product drugi raz.
No chyba, że gdzieś po drodze zmieniła się referencja... ale wtedy at zmiana musi być raczej u mnie w kodzie, .NET sam z siebie mi przecież referencji nie zmieni. A skoro u mnie w kodzie - to powinno się to zachowywać tak samo na obu kompach, nie?
Że implementacja GetHashCode może być różna albo niewłaściwa wręcz to wiem i rozumiem. Ale to nie rozwiązuje problemu :) Albo ja dalej czegoś nie rozumiem ;)

0

GetHashCode może zwracać różne wartości na różnych komputerach.
Z tego powodu mogła zaistnieć sytuacja, że u Ciebie dawało inną wartość niż u kogoś innego.

0

Dobrze, teraz już rozumiem o co Ci chodzi pako1337. Ale wynika z tego tyle, że błąd musi gdzieś tkwić w kodzie. Nie ma możliwość aby obiekt spod tej samej referencji zwracał inny HashCode. Gdzieś może zgubiła się metoda Clone(), albo Product jest strukturą. Trudno powiedzieć.

0

Product jest klasą. I faktycznie, może gdzieś tam w kodzie jest ukryty błąd, którego na razie nie znalazłem. Byłby to błąd o tyle dziwny, że całość programu działa, działa bardzo nawet dobrze (jak na ten poziom rozwoju), tylko z tą listą problem...
No i czemu na jednym kompie jest ok (czyli zakładam, że hashCode był zwracany dobrze), a na drugim już źle (czyli dostajemy różne hashe albo mamy po prostu różne referencje)? No chyba, że: mamy różne referencje zawsze, a na jednym kompie różne referencje dają różny hashCode, a na drugim zbiegiem okoliczności dają ten sam ;) Różnie to w sumie może być, tak tylko się interesowałem, bo może ktoś wie o co chodzi, albo się z czymś takim spotkał :)

0

@pako - spytaj na Codeguru.pl, tam jest ktoś, kto najprawdopodobniej będzie to wiedział.

0

GetHashCode może zwracać różne wartości na różnych komputerach.
Z tego powodu mogła zaistnieć sytuacja, że u Ciebie dawało inną wartość niż u kogoś innego.

To nie ma żadnego znaczenia dla autora postu.

Prawda - na różnych komputerach a nawet wersjach .NET Framework GetHashCode może zwrócić inną liczbę. Ale jeśli autor używa klasy, która nie nadpisuje GetHashCode - to używany jest GetHashCode z klasy bazowej. Jeśli tej nie ma - automatycznie jest to klasa Object.

Jeśli tak, to MSDN mówi:

For derived classes of Object, the GetHashCode method can delegate to the Object..::.GetHashCode implementation, if and only if that derived class defines value equality to be reference equality and the type is not a value type.

Czyli jest to praktycznie pytanie o referencję. Zatem nie może być tak (w tym przypadku!), że dwa wskaźniki wskazujące na ten sam obiekt na stercie mają różne GetHashCode.

Inna sprawa jeśli autor używałby struktury - wtedy musiałby nadpisać GetHashCode.

Wniosek : musiałeś coś pomylić :)

0

No jasne, tego świadom jestem :) . Ale dalej zastanawia mnie, jak ten sam kod u jednego działa poprawnie a u drugiego nie. Zakładając że coś pomyliłem, albo że gdzieś w kodzie po prostu jest tworzony nowy obiekt z takimi samymi polami jak stary i w ogóle, ale o innej referencji, to na obu kompach musiałoby to wyglądać tak samo. Czyli na obu powinny być różne obiekty i na obu Contains() powinno dawać false.
No chyba, że mamy przypadek skrajny, gdzie są różne referencje, ale na jednym kompie akurat tak się dziwnie składa, że implementacja GetHashCode() zwraca dla tych dwóch różnych obiektów te same hashe.

Istnieje też oczywiście jeszcze inna opcja - czynnik ludzki. Kumpel nie robił tego dokładnie tak jak mu mówiłem i się zamieszało ;)

0

No chyba, że mamy przypadek skrajny, gdzie są różne referencje, ale na jednym kompie akurat tak się dziwnie składa, że implementacja GetHashCode() zwraca dla tych dwóch różnych obiektów te same hashe.

To nie jest problem. GetHashCode ma obowiazek zwrocic takie same wartosci dla takich samych obiektow. Jesli zwroci to dopiero wtedy uzywana jest metoda Equals do stwierdzenia tozsamosci obiektow. To dla optymalizacji - jesli GetHashCode zwroci rozne wartosci, to Equals nie jest odpalana, bo nie ma po co.

0

A czy zaimplementowałeś metodę Equals dla klasy Product? Jeśli nie, to raczej w tym leży problem.

0

A czy zaimplementowałeś metodę Equals dla klasy Product? Jeśli nie, to raczej w tym leży problem.

Napisał, że ma jedną referencję - nie potrzeba nadpisywać Equals dla klasy produkt.

0

Dla tej samej referencji tym bardziej powinna byc zwracana taka sama wartosc. Moze sprobuj na chwile nadpisac GetHashCode i Equals i sprawdz czy to rzeczywiscie sa te same referencje?

0

po nadpisani equals i takich tam wszystko działa jak należy oczywiście, więc problem jest jako tako nieistniejący. Ale przyglądam sie mu i w wolnej chwili to posprawdzam :)

0

Najwyrazniej to nie byly te same referencje, ale takie same obiekty.

1 użytkowników online, w tym zalogowanych: 0, gości: 1