Nepaisyti „GetHashCode“ redaguojamiems objektams?

Aš perskaičiau apie 10 skirtingų klausimų apie tai, kada ir kaip iš naujo apibrėžti „ GetHashCode , bet aš vis tiek negaunu. Dauguma „ GetHashCode yra pagrįsti objekto laukų maišos kodais, tačiau buvo nustatyta, kad „ GetHashCode “ vertė niekada neturėtų keistis viso objekto gyvavimo ciklo metu. Kaip tai veikia, jei laukai, kuriais jis yra pagrįstas, yra keičiami? Taip pat, jei noriu ieškoti žodžių ir tt Remiantis referencine lygybe, o ne mano iš naujo apibrėžtu „ Equals ?

Pirmą kartą iš naujo apibrėžsiu lygiavertį vieneto, kuris bando mano serializacijos kodą, paprastumą, kuris, manau, serializuoja ir deserializuoja (XML atveju mano atveju) nužudo referencinę lygybę, todėl noriu įsitikinti, kad bent jau teisinga pagal lygybės vertę Ar šiuo atveju ši bloga praktika yra svarbesnė už „ Equals “? Iš esmės, daugumoje vykdytinų kodų noriu pateikti lygybės nuorodą, ir aš visada naudoju == , ir aš jo nepaisau. Ar turėčiau tiesiog sukurti naują metodą „ ValueEquals ar kažką, o ne ValueEquals Equals ? Manau, kad struktūra lyginant daiktus visada naudoja == , o ne „ Equals “, todėl maniau, kad būtų saugu iš naujo apibrėžti „ Equals , nes man atrodė, kad tai buvo numatyta, jei norite, kad antrasis lygybės apibrėžimas būtų kitoks nei operatorius == . Skaitydami keletą kitų klausimų, nors atrodo, kad taip nėra.

EDIT:

Atrodo, kad mano ketinimai buvo neaiškūs, turiu omenyje, kad 99% atvejų man reikia paprastos senos nuorodos lygybės, numatytojo elgesio, nenuostabu. Labai retais atvejais noriu turėti lygybės vertę, ir aš noriu aiškiai prašyti lygių vertybių naudojant .Equals o ne == .

Kai tai darau, kompiliatorius rekomenduoja iš naujo apibrėžti „ GetHashCode , taip pat tai, kaip kilo ši problema. Atrodo, kad „ GetHashCode “ prieštaraujantys tikslai taikytini keičiamiems objektams:

  • Jei a.Equals(b) , tada a.GetHashCode() turėtų == b.GetHashCode() .
  • a.GetHashCode() vertė niekada neturėtų keistis a .

Jie atrodo natūraliai nenuoseklūs, kai objektas keičiamas, nes jei pasikeičia objekto būklė, mes tikimės, kad .Equals() reikšmė pasikeis, o tai reiškia, kad GetHashCode turėtų keistis, atsižvelgiant į pasikeitimą. .Equals() , bet GetHashCode neturėtų būti keičiamas .

Kodėl tai atrodo prieštaringa? Šios rekomendacijos nėra skirtos keisti objektams? Tikriausiai manoma, bet galbūt verta paminėti, kad aš turiu omenyje klases, o ne struktūras.

Rezoliucija:

Aš pažymiu JaredPar kaip įprasta, bet daugiausia dėl komentarų sąveikos. Apibendrinant tai, ką sužinojau iš to, vienintelis būdas pasiekti visus tikslus ir išvengti galimo GetHashCode elgesio kraštutiniais atvejais yra panaikinti „ Equals ir „ GetHashCode pagrįstą nekintamais laukais, arba įdiegti „ IEquatable . Atrodo, kad šis požiūris sumažina naudojimą iš naujo apibrėžti „ Equals “ referenciniams tipams, nes tai, ką mačiau, dauguma referencinių tipų paprastai neturi nekintamų laukų, jei jie nėra saugomi reliacinėje duomenų bazėje, kad būtų galima juos identifikuoti su pagrindiniais raktais.

52
17 мая '09 в 3:53 2009-05-17 03:53 Davy8 yra nustatytas gegužės 17 d., 09:53 , 2009-05-17 03:53
@ 5 atsakymai

Kaip tai veikia, jei laukai, kuriuose jis yra pagrįstas, keičiasi?

Tai nėra ta prasme, kad maišos kodas pasikeis, kai keičiasi objektas. Tai yra problema dėl visų priežasčių, išvardytų jūsų skaitomuose straipsniuose. Deja, tai yra problema, kuri paprastai pasireiškia tik kampiniais atvejais. Todėl kūrėjai linkę vengti blogo elgesio.

Taip pat, jei noriu ieškoti žodžių ir tt grindžiamas lygiavertiškumu, o ne mano iš naujo apibrėžtu „Equals“?

Kol IEquatable<T> sąsają, pvz., „ IEquatable<T> , tai neturėtų būti problema. Daugumoje žodynų IEquatable<T> bus pasirinktas lygybės žemėlapis tokiu būdu, kuris naudos „ IEquatable<T> virš objekto.ReferenceEquals. Net ir be „ IEquatable<T> , dauguma pagal nutylėjimą vadinsis Object.Equals (), kuris bus įtrauktas į jūsų įgyvendinimą.

Iš esmės daugumoje vykdytinų kodų noriu pateikti nuorodos lygybę, ir aš visuomet naudoju ==, ir aš jų nepaisau.

Jei tikitės, kad jūsų objektai elgtųsi su lygybės verte, turite panaikinti == ir! = Užtikrinti lygias vertes visiems palyginimams. Vartotojai vis tiek gali naudoti „Object.ReferenceEquals“, jei jie iš tikrųjų nori informacijos apie lygybę.

Aš maniau, kad sistema visada naudoja ==, o ne „Equals“, kad palygintų dalykus

Koks BCL naudojimas pasikeitė laikui bėgant. Dabar dauguma lygybės naudojimo atvejų turės IEqualityComparer<T> egzempliorių ir naudos ją lygybei. Tais atvejais, kai vienas iš jų nenurodytas, jie naudos „ EqualityComparer<T>.Default . Blogiausiu atveju tai sukels Object.Equals pagal nutylėjimą.

20
17 мая '09 в 4:07 2009-05-17 04:07 atsakymą pateikė JaredPar gegužės 17 d. 09:07 2009-05-17 04:07

Jei turite kintamą objektą, nėra prasmės perimti GetHashCode metodą, nes negalite jo naudoti. Pavyzdžiui, jis naudojo Dictionary ir „ HashSet rinkinius, kad kiekvienas elementas būtų patalpintas į kibirą. Jei pakeisite objektą, kai jis naudojamas kaip raktas rinkinyje, maišos kodas nebeatitinka kibiro, kuriame yra objektas, todėl kolekcija neveikia tinkamai ir niekada nerandate objekto.

Jei norite, kad paieška nebūtų naudojama „ GetHashCode arba „ Equals metodui, visada galite pateikti savo „ IEqualityComparer skirtą naudoti, kai kuriate Dictionary .

Equals metodas skirtas vertybių lygybei, todėl neteisinga tai įgyvendinti.

6
17 мая '09 в 4:21 2009-05-17 04:21 atsakymas, kurį pateikė Guffa gegužės 17 d. , 09:21, 2009-05-17 04:21

Oho, kas iš tikrųjų yra keletas klausimų viename :-). Taigi, po vieną:

buvo teigiama, kad GetHashCode vertė niekada neturėtų keistis per visą objekto gyvavimo ciklą. Kaip tai veikia, jei laukai, kuriais jis yra pagrįstas, yra keičiami?

Šis bendras patarimas yra skirtas, kai norite naudoti savo objektą kaip raktą HashTable / žodynuose ir tt Paprastai maišymo lentelės reikalauja, kad maišos nebūtų pakeistos, nes jos naudojasi sprendžiant, kaip saugoti ir gauti raktą. Jei maišos pasikeičia, „HashTable“ tikriausiai nebebus jūsų objekto.

„Java“ žemėlapio sąsajos dokumentų pateikimas:

Pastaba Turėtų būti labai atsargūs, jei kintamieji objektai yra naudojami kaip žemėlapio raktai. Žemėlapio elgesys nenurodomas, jei objekto vertė pasikeičia taip, kad paveiks vienodus palyginimus, jei objektas yra raktas žemėlapyje.

Apskritai, bloga idėja naudoti bet kokį kintamąjį objektą kaip raktą maišos lentelėje: ji net nesupranta, kas turėtų įvykti, jei raktas pasikeistų po to, kai jis bus pridėtas prie maišos lentelės. Jei maišos lentelė grąžina išsaugotą objektą per senąjį raktą arba per naująjį raktą arba abu?

Taigi, tikras patarimas: naudokite tik nekintamus objektus kaip raktus ir įsitikinkite, kad jų maišos kodas niekada nepasikeičia (paprastai tai automatiškai, jei objektas nekeičiamas).

Taip pat, jei noriu ieškoti žodžių ir tt remiantis referencine lygybe, o ne mano iš naujo apibrėžtu „Equals“?

Na, suraskite tokį žodyną. Tačiau standartiniai bibliotekų žodynai naudoja hashcode Equals, ir nėra jokio būdo tai pakeisti.

Pirmą kartą iš naujo apibrėžsiu lygiavertį vieneto, kuris bando mano serializacijos kodą, paprastumą, kuris, manau, serializuoja ir deserializuoja (XML atveju mano atveju) nužudo referencinę lygybę, todėl noriu įsitikinti, kad jis yra bent teisingas lygybės požiūriu. Ar ši bloga praktika šiuo atveju yra „Equals“ atšaukimas?

Ne, manau, kad tai visiškai priimtina. Tačiau žodyną / maišos lentelę neturėtumėte naudoti tokių objektų kaip raktai, nes jie yra keičiantys. Žr. Aukščiau.

3
17 мая '09 в 4:13 2009-05-17 04:13 atsakė sleske gegužės 17 d., 09:13 , 2009-05-17 04:13

Aš nežinau apie „C #“, nes tai yra santykinis „Noob“, tačiau „Java“, jei nepaisysite lygių (), jūs taip pat turite ignoruoti hashCode (), kad išlaikytumėte sutartį tarp jų (ir atvirkščiai). Ir java taip pat turi tą patį triuką 22; iš esmės verčia jus naudoti nekintamus laukus ... Bet tai tik problema klasėms, naudojamoms kaip maišos raktas, o Java turi alternatyvius realizavimo būdus visiems maišos rinkiniams ... kurie gali būti ne tokie greitai, bet jie leidžia naudokite kintamą objektą kaip raktą ... tai tiesiog (dažniausiai) nuskubo kaip „blogas dizainas“.

Ir aš jaučiu, kad noriu pabrėžti, kad ši pagrindinė problema yra amžina ... Tai buvo, nes Adomas buvo vaikinas.

Aš dirbau kodo fortrane, kuris yra vyresnis nei aš (aš esu 36), kuris sulaužo, kai keičiasi vartotojo vardas (pavyzdžiui, kai mergaitė susituokia arba išsiskyrė; -) ... Taigi, inžinierius, sprendimas buvo toks: „GetHashCode“ metodas GetMashCode prisimena anksčiau apskaičiuotą maišos kodą, perskaičiuoja maišos kodą (t.y. virtualų žymeklį -deštąjį) ir, jei pagrindiniai laukai pasikeitė, jis grąžina nulį. Dėl to talpykla pašalina nešvarų vartotoją (paskambindama kitu GetPreviousHashCode), o tada talpykla grąžina nulį, verčia vartotoją perskaityti iš duomenų bazės. Įdomus ir naudingas įsilaužimas; net jei aš taip sakau; -)

Aš naudosiu variaciją (tik pageidautina kampiniais atvejais), kad galėčiau pasiekti O (1) (pageidautina visais atvejais). Sveiki atvykę į inžineriją; tyčinio kompromiso šalis.

Sveikinimai. Banginis

1
17 мая '09 в 9:57 2009-05-17 09:57 atsakė korlettui gegužės 17 d. 09 val. 09:57 2009-05-17 09:57

Pagrindinė tema yra tai, kaip geriausiai nustatyti objektus. Jūs kalbate apie serializaciją / deserializaciją, kuri yra svarbi, nes šiame procese prarandamas referencinis vientisumas.

Trumpas atsakymas yra toks: ar šie objektai turi būti vienareikšmiškai identifikuojami pagal mažiausius nepakeičiamus laukus, kuriuos galima panaudoti šiam tikslui atlikti. Tai yra laukai, kuriuos privalote naudoti nepaisydami „GetHashCode“ ir „Equals“.

Atliekant bandymus, yra visiškai pagrįsta nustatyti bet kokius reikalingus pareiškimus, paprastai jie nėra nustatomi pagal paties tipo, o kaip bandymų rinkinio komunalines paslaugas. Gal TestSuite.AssertEquals (MyClass, MyClass)?

Atkreipkite dėmesį, kad GetHashCode ir Equals turėtų veikti kartu. GetHashCode turėtų grąžinti tą pačią vertę dviem objektams, jei jie yra lygūs. Lygiai turi grįžti tiesa, jei ir tik tada, kai du objektai turi tą patį maišos kodą. (Atkreipkite dėmesį, kad gali būti, kad du objektai negali būti lygūs, bet jie gali grąžinti tą patį maišos kodą). Yra daug tinklalapių, kurie išsprendžia šią temą, tiesiog atidarykite „Google“.

1
17 мая '09 в 4:35 2009-05-17 04:35 atsakė Joe gegužės 17 d., 09:35, 2009-05-17 04:35

Kiti klausimai apie žymes arba Užduoti klausimą