[ vujkev @ 13.04.2019. 01:00 ] @
U jednom Xamarin projektu već satima se vrtim oko gluposti i počinjem da verujem da me neko z...

Dakle sa linq tražim neki element u kom value ima ID određene vrednosti. Promenljiva "pin" nakon prve linije ima neku vrednost (dakle element je nađen), ali kad proverim da li dictionary sadrži key dobijem podatak da isti nije nađen. Osećam da je rešenje izuzetno glupo i jednostavno, ali ga jednostavno ne vidim.

Code:


//u MyMap je definisan dictionary kao 
public Dictionary<Pin, Data> Pins { get; } = new Dictionary<Pin, Data>();
//Pin i Data su interne klase

//kad pokušam da nađem neki element 
         var pin = MyMap.Pins.Where(pp => pp.Value.ID == item.ID).First();
                if (pin.Key == null)
                {
// nije null
                }
                if (MyMap.Pins.ContainsKey(pin.Key) == false)
                {
// NE POSTOJI!!!!!
                }


edit: pokušavam da nađem koji je tag za C# kod, ali mi ne ide. Možda je vreme za spavanje

da budem precizniji, Pin je ova klasa: https://github.com/amay077/Xam...amarin.Forms.GoogleMaps/Pin.cs


[Ovu poruku je menjao vujkev dana 13.04.2019. u 02:11 GMT+1]
[ S A J A @ 13.04.2019. 11:28 ] @
Zato ja ne volim C#, ima koliko hoćeš ovakvih gluposti.... ;)

Pogledaj:

Dictionary ContainsKey returns false when should be true?

ContainsKey() dont find an object in Dictionary


Ukratko: ako je lista objekata, onda ti Key nije sam po sebi postojeći dok nešto ne implementiraš... a ti dalje guglaj šta ;)
[ _owl_ @ 13.04.2019. 11:43 ] @
Koliko vidim za ključeve u rečniku koristiš objekte tipa Pin, u tom slučaju podrazumevano se jednakost objekata koji predstavljaju ključeve proverava po referenci (tj. ako uzmeš dva objekta koji imaju iste "podatke" oni nisu jednaki). U svojoj Pin klasi možeš implementirati metodu Equals (nasleđeno od tipa Object) koja će vršiti poređenje ključeva.
[ Shadowed @ 13.04.2019. 12:13 ] @
Code:

if (MyMap.Pins.ContainsKey(pin.Key) == false)
{
// NE POSTOJI!!!!!
}


Cek', je l' tebi zapravo udje u taj if blok?
[ vujkev @ 13.04.2019. 12:40 ] @
DA!!!!

Na kraju sam odustao od ovoga i napravio dva dictionary objekta gde je Key u oba int, a value u jednom Pin, a u drugom Data. Radi iz prve.

Naravno voleo bih da znam rešenje ovog problema, ako ga uopšte i ima

Citat:
_owl_

Naravno da se gleda referenca, to mi je apsolutno jasno, ali ja ovde iz kolekcije uzmem objekat i odmah pitam postoji li taj objekat. Nigde između ne pravim novi objekat ili radim bilo šta sa njim
[ vujkev @ 13.04.2019. 12:43 ] @
Citat:
S A J A:
Zato ja ne volim C#, ima koliko hoćeš ovakvih gluposti.... ;)

Pogledaj:

Dictionary ContainsKey returns false when should be true?

ContainsKey() dont find an object in Dictionary


Ukratko: ako je lista objekata, onda ti Key nije sam po sebi postojeći dok nešto ne implementiraš... a ti dalje guglaj šta ;)


Čitao sam na tu temu (možda ne sa ovih sajtova koje si ti stavio) i koliko se sećam neophodno je se implementira == operator i Equale metoda. Koliko vidim u Pin klasi sve je to urađeno kako treba (nije moja klasa pa sam zato i postavio link do iste na github-u), pa mi nije jasno zašto opet ne nalazi objekat.
[ Shadowed @ 13.04.2019. 13:30 ] @
Citat:
vujkev: DA!!!!

OK, pitao sam posto je malo neuobicajeno da se if pravi sa == false pa da proverimo da nije tu greska :)

Citat:
vujkev: Čitao sam na tu temu (možda ne sa ovih sajtova koje si ti stavio) i koliko se sećam neophodno je se implementira == operator i Equale metoda. Koliko vidim u Pin klasi sve je to urađeno kako treba (nije moja klasa pa sam zato i postavio link do iste na github-u), pa mi nije jasno zašto opet ne nalazi objekat.

Mislim da je problem upravo u tome sto su implementirane te stvari. Konkretnije, GetHashCode().

Elem, Dictionary<TKey, TValue> klasa je implementacija HashTable strukture podataka i funkcionise tako sto napravi tabelu hash-ova i povezuje sa vrednostima. Dakle, za Key ne koristi sam objekat nego ono sto dobije pozivanjem njegovog .GetHashCode metoda.
Zasto dolazi do problema? Zato sto je GetHashCode u Pin klasi implementiran tako da zavisi od property-a i onda dodjes u sledecu situaciju:
-Kreira se Dictionary
-Kreira se Pin
-Pin se iskoristi kao key, sto ce reci, njegov hash se iskoristi kao key
-Neki od poperty-a u Pin-u od kojih se kreira hash se promeni
-Nadjes KeyValuePair objekat iz dictionary-a na osnovu vrednosti (ovo si dao u kodu)
-Proveris da li Key iz tog para postoji.
-Dictionary uzme hash tog Key-a da bi ga potrazio u svojoj tabeli ali je on sad drugaciji (jer se promenio neki property) i ne moze da ga nadje.


Jedini deo koji ne mogu da proverim je da li je doslo do promene property-a (jer se kod izvrsava kod tebe) ali mi se cini da je tako :)
[ bokinet @ 13.04.2019. 16:16 ] @
Apro GetHashCode() info. dodatak:

Ako se pogleda MS zvanicna dokumentacija sam MS predlaze da se preterano ne racuna na te Hash f-je vec da dev. napravi i implementira svoje...
[ mjanjic @ 13.04.2019. 16:22 ] @
Možda umesto ContainsKey da koristiš TryGetValue, koji doduše vraća Exception Error, ali to odradiš sa try/catch.

Da li si probao sa debagerom da vidiš koje vrednosti uzima "pin"?
[ dejanet @ 13.04.2019. 16:34 ] @
Za key od dictionary lose je koristi complex object, jer onda uleces u problem sa poredjenjem dva objekata (vidim vec pricate o hash kodu, ludilo).

Za key neki primitive, eventualno string.

Code u prvom postu nema blage veze sa zivotom.
[ Shadowed @ 13.04.2019. 19:01 ] @
Nije problem koristiti objekat per-se. Problem je ako koristis objekat i taj objekat ima override-ovan GetHashCode() tako da zavisi od property-a i taj property se promeni u medjuvremenu.
Resenje u ovom slucaju je da ili cuva na drugi nacin ili (u teoriji) nasledi Pin klasu. No u praksi ne moze jer ju je autor napravio kao sealed.
[ Branimir Maksimovic @ 13.04.2019. 19:18 ] @
Zar ta klasa Dictionary nije dokumentovana? Svudge u hash tabeli ako stavljas za kljuc custom strukturu moras obezbediti hash f-ju i operaciju jednakosti.
[ Shadowed @ 13.04.2019. 19:24 ] @
GetHashCode() metod postoji u klasi Object koju sve klase nasledjuju po dizajnu tako da ne moras implementirati nista.
Problem je sto ta klasa Pin jeste implementirala i to lose (hash joj je promenljiv).
Dictionary jeste dokumentovan i tamo i pise da to ne sme da se radi (mada je losa praksa i nezavisno od Dictionary-a).

Citat:
Retrieving a value by using its key is very fast, close to O(1), because the Dictionary<TKey,TValue> class is implemented as a hash table.
As long as an object is used as a key in the Dictionary<TKey,TValue>, it must not change in any way that affects its hash value

https://docs.microsoft.com/en-...DN&view=netframework-4.7.2
[ dejanet @ 13.04.2019. 19:37 ] @
U prvom koraku moze transformise Pin u listu "flat" custom objekata, npr moze da koristi......Select(new NekaViewKlasa...

Zatim u drugom koraku moze da kreira jednostavne upite nad tom flaty listom i/ili dictionary.
npr.
var NekaViewKlasa= NekaViewKlasaLista.FirstOrDefault(pp => pp.ID == item.ID);
if (NekaViewKlasa == null)
{
//Ne postoji
}

BTW, gore kodu u skoro svakoj liniji moze da fasuje null exception.

EDIT:

Bacio sam "quick view" na Pin Xamarin klasu. Meni lici da je GetHashCode() implementran tako sa razlogom u funkciji properties Label, Position, Type, Address. Dalje treba kopati zasto je tako, ali nije greska, mislim.

[Ovu poruku je menjao dejanet dana 13.04.2019. u 20:54 GMT+1]
[ vujkev @ 13.04.2019. 20:39 ] @
Citat:
Shadowed:
Jedini deo koji ne mogu da proverim je da li je doslo do promene property-a (jer se kod izvrsava kod tebe) ali mi se cini da je tako :)


Mislim da je ovo odgovor na moj problem. Property se menja u kodu.
Ne mogu sad da testiram da li bi to rešilo problem, pošto sam izmenio kod poprilično, a ne vraća mi se nazad pošto sve radi kako treba.


Inače moj primer je naravno totalno glup i nefunkcionalan, ali sinoć je bilo daj šta daš da uhvatim/objasnim šta je problem. U svakom slučaju očigledno je bio dovoljan da nađete problem :)


Hvala svima

Poz.
[ Shadowed @ 13.04.2019. 20:43 ] @
Ako se menja, to je definitivno razlog. Probao sam ja sa izmenom property-a i bez i kad menjas ne nalazi ga a kad ne menjas, nadje.
Mozda da javis i tom autoru da postoji taj problem. Ako je vec dao svoj kod za koriscenje besplatno, red je i da mu se malo pomogne :)


@SAJA, kao sto vidis, nema nikakve veze sa C#-om :)
[ mjanjic @ 14.04.2019. 08:31 ] @
Ima veze sa OOP i načinom na koji se koristi ono što je ugrađeno u određen framework baziran na određenom jeziku.
[ Shadowed @ 14.04.2019. 10:01 ] @
Problem je u tome sto implementacija klase Pin nije kompatibilna sa upotrebom iste kao kljuca u klasi Dictionary. Nema veze sa jezikom. To je bilo povodom:
Citat:
S A J A: Zato ja ne volim C#, ima koliko hoćeš ovakvih gluposti.... ;)



Isti problem bi imao i u VB.NET-u koji on koristi.
[ dusans @ 14.04.2019. 10:42 ] @
Hajde na stranu što postoje tehničke začkoljice oko korištenja objekta kao ključa u dictionary-ju...

Pravo pitanje je koje probleme OP rešava i da li je pristup dobar?

Iz prvog primera koda se vidi da se na osnovu Data.ID traži njemu pridružen Pin.
Da li postoji i potreba za obrnutim - da se na osnovu Pin-a traži njemu pridružena Data?
(jer ako ne postoji, onda nema ni potrebe da key bude Pin)
itd...
Sve mi ovo vuče pomalo na XY problem:
http://xyproblem.info/

Dakle, daj da vidimo šta je problem i naizgled "potrebne" operacije za rešavanje,
pa na osnovu toga predložimo odgovarajući pristup rešenju (umesto da budžimo dictionary).
[ mmix @ 14.04.2019. 12:39 ] @
Dictionary zahteva immutable objekte ili objekte sa fiksnim jedinstvenim random hashom ili hashom vezanim za immutable property(ies). To je zato sto u pozadini koristi Hash buckets da optimizuje pronalazenje elemenata i svede na O(1) umesto O(n). Tako da nije problem ni u equality checking nego u cinjenici da ako se hash promeni Dictionary ce pokusati da element pronadje u pogresnom bucketu i vratice da ga nema, equality comparator se koristi samo unutar istog bucketa da se locira element (ako bucket ima vise od 1 elementa).

Za kljuc koristite imutable objekte ili value tipove (eventualno fabricki immutable tipove kao string). To neam veze sa C#-om vec sa CLR.

Code (csharp):

        private int FindEntry(TKey key) {
            if( key == null) {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
            }
 
            if (buckets != null) {
                int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
                for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
                    if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
                }
            }
            return -1;
        }
 


https://referencesource.micros...dictionary.cs,602c049c4edafd6d

[Ovu poruku je menjao mmix dana 14.04.2019. u 13:59 GMT+1]
[ djordjeno @ 15.04.2019. 10:40 ] @
Da, ovde je greska u odluci sta ce biti Key.

Koliko sam razumeo "Pin" klasa je neki UX element koji zivi na formi i gde atributi tog elementa igraju glavnu ulogu u smislu hash funkcije.
(koja se zove za proveru proveru prisutnosti u dictionary)
Kao sto predlazu drugi za key treba koristiti primitivni tip, ili napraviti novu klasu koja ce imati u sebi instancu Pin-a i pored neki identifikator koji je unique u kontekstu gde su svi ostali. I taj identifikator koristiti da bude key. Naravno u tom slucaju kontrola kako se dodeljuje identifikator je odgovornost programera, a ne krajnjeg korisnika (u smislu da preko UX-a promeni key)