Bagaimana cara kerja implementasi default GetHashCode()
? Dan apakah itu menangani struktur, kelas, array, dll secara efisien dan cukup baik?
Saya mencoba memutuskan dalam hal apa saya harus mengemas sendiri dan dalam kasus apa saya dapat dengan aman mengandalkan implementasi default untuk melakukannya dengan baik. Saya tidak ingin menemukan kembali roda, jika memungkinkan.
.net
hash
gethashcode
Fung
sumber
sumber
GetHashCode()
telah ditimpa) dengan menggunakanSystem.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj)
Jawaban:
InternalGetHashCode dipetakan ke fungsi ObjectNative :: GetHashCode di CLR, yang terlihat seperti ini:
Implementasi penuh GetHashCodeEx cukup besar, jadi lebih mudah untuk hanya menautkan ke kode sumber C ++ .
sumber
string
penggantianGetHashCode
. Di sisi lain, anggaplah Anda ingin menghitung berapa kali berbagaiPaint
peristiwa proses kontrol . Anda dapat menggunakanDictionary<Object, int[]>
(setiapint[]
disimpan akan menyimpan tepat satu item).Untuk sebuah kelas, default pada dasarnya adalah referensi kesetaraan, dan itu biasanya baik-baik saja. Jika menulis struct, lebih umum untuk mengesampingkan kesetaraan (paling tidak untuk menghindari tinju), tetapi sangat jarang Anda menulis struct juga!
Ketika mengesampingkan kesetaraan, Anda harus selalu memiliki kecocokan
Equals()
danGetHashCode()
(yaitu untuk dua nilai, jikaEquals()
mengembalikan true mereka harus mengembalikan kode hash yang sama, tetapi sebaliknya tidak diperlukan) - dan biasanya juga disediakan==
/!=
operator, dan sering kali untuk terapkanIEquatable<T>
juga.Untuk menghasilkan kode hash, adalah umum untuk menggunakan jumlah faktor, karena ini menghindari tabrakan pada nilai-nilai berpasangan - misalnya, untuk hash bidang 2 dasar:
Ini memiliki keuntungan bahwa:
dll - yang umum jika hanya menggunakan jumlah tidak tertimbang, atau xor (
^
), dll.sumber
unchecked
. Untungnya,unchecked
ini adalah default dalam C #, tetapi akan lebih baik untuk membuatnya eksplisit; dieditDokumentasi untuk
GetHashCode
metode untuk Objek mengatakan "implementasi default metode ini tidak boleh digunakan sebagai pengidentifikasi objek unik untuk tujuan hashing." dan yang untuk ValueType mengatakan "Jika Anda memanggil metode GetHashCode tipe turunan, nilai kembali kemungkinan tidak cocok untuk digunakan sebagai kunci dalam tabel hash." .Tipe data dasar seperti
byte
,short
,int
,long
,char
danstring
menerapkan metode yang baik GetHashCode. Beberapa kelas dan struktur lain, sepertiPoint
misalnya, menerapkanGetHashCode
metode yang mungkin cocok atau tidak cocok untuk kebutuhan spesifik Anda. Anda hanya perlu mencobanya untuk melihat apakah itu cukup baik.Dokumentasi untuk setiap kelas atau struktur dapat memberi tahu Anda apakah itu menimpa implementasi standar atau tidak. Jika tidak menimpanya, Anda harus menggunakan implementasi Anda sendiri. Untuk setiap kelas atau struct yang Anda buat sendiri di mana Anda perlu menggunakan
GetHashCode
metode ini, Anda harus membuat implementasi Anda sendiri yang menggunakan anggota yang sesuai untuk menghitung kode hash.sumber
Karena saya tidak dapat menemukan jawaban yang menjelaskan mengapa kita harus mengganti
GetHashCode
danEquals
untuk custom structs dan mengapa implementasi default "sepertinya tidak cocok untuk digunakan sebagai kunci dalam tabel hash", saya akan meninggalkan tautan ke blog ini posting , yang menjelaskan mengapa dengan contoh kasus nyata dari masalah yang terjadi.Saya sarankan membaca seluruh posting, tetapi di sini adalah ringkasan (penekanan dan klarifikasi ditambahkan).
Alasan hash default untuk struct lambat dan tidak terlalu baik:
Masalah dunia nyata yang dijelaskan dalam pos:
Jadi, untuk menjawab pertanyaan "dalam kasus apa saya harus mengemas sendiri dan dalam kasus apa saya dapat dengan aman mengandalkan implementasi default", setidaknya dalam kasus struct , Anda harus mengganti
Equals
danGetHashCode
kapan pun struct kustom Anda dapat digunakan sebagai kunci dalam tabel hash atauDictionary
.Saya juga merekomendasikan menerapkan
IEquatable<T>
dalam hal ini, untuk menghindari tinju.Seperti jawaban lain mengatakan, jika Anda menulis kelas , hash default menggunakan referensi kesetaraan biasanya baik-baik saja, jadi saya tidak akan repot dalam hal ini, kecuali jika Anda perlu menimpa
Equals
(maka Anda harus menimpa yangGetHashCode
sesuai).sumber
Secara umum, jika Anda mengganti Equals, Anda ingin mengganti GetHashCode. Alasan untuk ini adalah karena keduanya digunakan untuk membandingkan persamaan kelas / struct Anda.
Persamaan digunakan saat memeriksa Foo A, B;
jika (A == B)
Karena kita tahu bahwa pointer cenderung tidak cocok, kita dapat membandingkan anggota internal.
GetHashCode umumnya digunakan oleh tabel hash. Kode hash yang dihasilkan oleh kelas Anda harus selalu sama untuk status pemberian kelas.
Saya biasanya melakukannya,
Beberapa orang akan mengatakan bahwa kode hash hanya boleh dihitung sekali per objek seumur hidup, tapi saya tidak setuju dengan itu (dan saya mungkin salah).
Menggunakan implementasi default yang disediakan oleh objek, kecuali jika Anda memiliki referensi yang sama ke salah satu kelas Anda, mereka tidak akan sama satu sama lain. Dengan mengganti Equals dan GetHashCode, Anda dapat melaporkan kesetaraan berdasarkan nilai internal daripada referensi objek.
sumber
Jika Anda hanya berurusan dengan POCO, Anda dapat menggunakan utilitas ini untuk menyederhanakan hidup Anda:
...
sumber