Referencja do obiektu

0

Cześć, zauważyłem rzecz, które mnie zaniepokoiła.
Mam sobie jakiś obiekt (m_items), który zawiera listę innych obiektów. I chcę na tych obiektach wykonać pewne operacje.

Teraz w innej klasie pobieram sobie jakiś obiekt:

 
MyItem m_supeItem = null;

public void UpdateSuperItem()
{
    for(int i = 0; i < m_items.Count; i++)
        if(cos_tam)
            m_superItem = m_items[i];
}

Po tym kodzie spodziewałem się, że m_superItem będzie trzymał referencję do konkretnego itemu na liście. Ale okazało się, że nie do końca tak jest. Po jakimś czasie i iluś operacjach sprawdzam:

 
if(Object.ReferenceEquals(m_superItem, m_items[i])) ...

I na debugerze widzę, że m_superItem w tym momencie to jest właśnie ten mój m_items[i] (określam to na podstawie pewnych właściwości). Ale ReferenceEquals zwraca false.

Jak to jest z tymi referencjami?

1

a MyItem nie jest czasem struct? jesli jest to sorry ale zawsze false bedziesz mial bo przy wywolaniu ReferenceEquals porownywane wartosci beda boxowane (co oznacza ze beda rozne). jesli nie masz tam struct to w takim razie daj caly kod.

0

Kod jest dość spory. Ale co może być jeszcze istotne, to to, że lista się zmienia. Są z niej usuwane pozycje i dodawane inne. Ale ten jeden element ma nie być usuwany. Dlatego takie sprawdzenie. I okazuje się, że w pewnym momencie jest usuwany, bo ReferenceEquals daje false. Wszystko jest na klasach, a nie na strukturach.

0

Coś robisz źle. A bez kodu nie da się stwierdzić, co dokładnie.

0

Zastrzege, że nie jestem pewien mojej "teorii".

Byc moze wraz z operacjami odbyla sie relokacja kolekcji.
Poniewaz pierwotne miejsce w pamieci (poza zmienna wskazujaca na liste) mialo dodatkowa referencje (bylo mozliwie do uzyskania) to GC je zostawil a dodatkowo nie ma problemu z naruszaniem pamieci.

Z tego co pamietam, m.in w c++ w czesci kolekcji (np w vectorze) po wstawianiu/usuwaniu elementow iteratory traca waznosc.

0

No tak, zapomniałem, że błędny kod, to nigdy nie jest wina programisty. Zawsze winny jest kompilator, runtime albo GC.

Zepsułeś coś w swoim kodzie. Albo coś źle sprawdzasz, albo czymś manipulujesz bardziej niż Ci się wydaje. Dopóki korzystasz ze zmiennej, dopóty GC jej nie ruszy.

0

Po jakimś czasie i iluś operacjach sprawdzam:

i w tym „jakimś czasie” nie ulega zmianie np. i?

bez kodu możemy tylko wróżyć.

0

skoro dodajesz i usuwasz elementy z tablicy to zapewne ten Twój element się przesuwa - dostaje indeks o 1 mniejszy lub większy - zapewne masz jakiś głupi błąd w kodzie. Możemy zapewnić że kompilator i środowisko działa poprawnie a to z Tobą jest problem

0

Próbowałem wyśledzić co się dzieje i dzieje się coś osobliwego.
Otóż tak. Jest to algorytm genetyczny.
Mam na liście 14 elementów (osobników).

Z tej listy wybieram najlepszego i chcę, żeby on na tej liście został. W pewnym momencie dochodzi do "kopulacji" między osobnikami. Potem potomków wymieniam na rodziców (chyba, że rodzicem był najlepszy osobnik, wtedy go zostawiam). Wygląda to tak:

 
public void ExchangeChildrenForParents(Creature parent1, Creature parent2, Creature child1, Creature child2, Creature bestCreature)
{
	bool parent1IsBest = (bestCreature != null) && (Object.ReferenceEquals(parent1, bestCreature));
	bool parent2IsBest = (bestCreature != null) && (Object.ReferenceEquals(parent2, bestCreature));
			
	if(!parent1IsBest)
        	m_population.Remove(parent1);

	if(!parent2IsBest)
		m_population.Remove(parent2);

        m_population.Add(child1);
        m_population.Add(child2);
}

Nie ma żadnej magii. Sprawdzam, czy mam ustawionego najlepszego osobnika, a jeśli tak, sprawdzam, czy to może jest parent1 lub parent2.

Następnie, jeśli parent1 NIE JEST najlepszym osobnikiem, to go usuwam. Potem analogicznie robię z parentem2. Na koniec do listy (populacji) dodaję dzieci.

I z jakiegoś powodu w momencie usuwania czasami usuwa mi najlepszego osobnika. Sprawdziłem i parent1IsBest i parent2IsBest zawsze przyjmują dobre wartości. Więc o co chodzi? Czemu z listy usuwa mi element, którego nie powinno w ogóle ruszać?

Podczas debugowania posługuję się funkcją "Make object ID".
Zauważyłem jeszcze jedną rzecz. Ale to chyba akurat normalne. Po usunięciu takim, gdzie zostaje mi najlepszy osobnik, w zmiennych lokalnych w debugerze cała lista jest widoczna na czerwono, oprócz tego właśnie osobnika. Na czerwono, czyli że została zmodyfikowana. W momencie, gdy usuwa mi się najlepszy osobnik, cała lista - wszystkie elementy bez wyjątku - są pokazane na czerwono.

Oczywiście referencje w innych miejscach do tego najlepszego obiektu zostają. Więc pewnym rozwiązaniem, ale oszukanym, jest sprawdzenie na koniec, czy najlepszy osobnik znajduje się na liście. A jeśli nie, to go trzeba dodać. No, ale chcę wiedzieć, gdzie tak naprawdę leży problem.

0

Chyba ogarnąłem. Wszystkiemu winna jest metoda List.Remove(T).
Otóż moja klasa Creature implementuje interfejsy: IComparable i IEquatable. Przeciąża też operatory, m.in. równości i nierówności, a także metodę Equals. W związku z tym, List.Remove(T) nie bierze pod uwagę referencji, tylko to, co zwraca metoda Equals, co w moim przypadku jest niepożądane. Więc jedyne, co pozostaje, to nie używać tej metody, tylko szukać i usuwać własnoręcznie konkretny element. Chyba, że można zrobić to lepiej?

1

Skoro Twoja implementacja Equals uznaje różne obiekty z identyczne, to znaczy, że jest niepoprawna. Popraw implementację tak, aby faktycznie stwierdzała czy obiekty są identyczne.

I to nie List.Remove jest winna tylko Ty, bo to Ty napisałeś złe Equals. ;]

0

Tak, myślałem że Equals ma robić to, co ==, bo sporo przykładów w necie tak jest skonstruowanych. Albo w drugą stronę. Teraz się okazuje, że Equals zawsze powinna sprawdzać referencję.

2

Nie. Equals ma sprawdzać identyczność obiektu. Czasami wystarczy referencja (i tak to domyślnie działa dla obiektów referencyjncych), czasami Id obiektu, czasami wszystkie właściwości, czasami jeszcze coś innego. Zadaniem programisty jest zdefiniowanie Equals tak, aby działało prawidłowo.

A, że operator == to to samo co Equals to raczej oczywiste, nie ma żadnego sensu, żeby było inaczej.

Jeśli u Ciebie Equals zwraca true dla różnych obiektów, to znaczy, że jest źle zaimplementowane. Musisz przemyśleć zasady według których stwierdzasz identyczność obiektu. Cały framework używa Equals do porównywania identyczności obiektów, więc trzeba przemyśleć działanie tej metody. Nie nadpisuje się jej w celu porównywania obiektu według jakiś specyficznych biznesowych reguł, do tego lepiej napisać własną metodę.

0

Już doszedłem z tym do porządku. Chcę, żeby operatory porównywały wartość funkcji przystosowania danego osobnika, natomiast Equals ma sprawdzać (jak się okazało) referencje.

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