Mengingat bahwa koleksi seperti System.Collections.Generic.HashSet<>menerima nullsebagai anggota set, orang dapat bertanya apa kode hash yang nullseharusnya. Sepertinya kerangka tersebut menggunakan 0:
// nullable struct type
int? i = null;
i.GetHashCode(); // gives 0
EqualityComparer<int?>.Default.GetHashCode(i); // gives 0
// class type
CultureInfo c = null;
EqualityComparer<CultureInfo>.Default.GetHashCode(c); // gives 0
Ini bisa (sedikit) bermasalah dengan enum nullable. Jika kita mendefinisikan
enum Season
{
Spring,
Summer,
Autumn,
Winter,
}
maka Nullable<Season>(juga disebut Season?) dapat mengambil hanya lima nilai, tetapi dua di antaranya, yaitu nulldan Season.Spring, memiliki kode hash yang sama.
Sangat menggoda untuk menulis pembanding kesetaraan yang "lebih baik" seperti ini:
class NewNullEnumEqComp<T> : EqualityComparer<T?> where T : struct
{
public override bool Equals(T? x, T? y)
{
return Default.Equals(x, y);
}
public override int GetHashCode(T? x)
{
return x.HasValue ? Default.GetHashCode(x) : -1;
}
}
Tapi adakah alasan mengapa kode hash nullharus 0?
EDIT / TAMBAH:
Beberapa orang sepertinya berpikir ini tentang menimpa Object.GetHashCode(). Sebenarnya tidak. (Penulis NET memang membuat override dari GetHashCode()dalam Nullable<>struct yang merupakan relevan, meskipun.) Implementasi ditulis pengguna dari parameterless yang GetHashCode()tidak pernah dapat menangani situasi di mana objek yang kode hash yang kita cari adalah null.
Ini tentang mengimplementasikan metode abstrak EqualityComparer<T>.GetHashCode(T)atau mengimplementasikan metode antarmuka IEqualityComparer<T>.GetHashCode(T). Sekarang, saat membuat tautan ini ke MSDN, saya melihat bahwa dikatakan di sana bahwa metode ini melempar ArgumentNullExceptionjika satu-satunya argumen mereka null. Ini pasti kesalahan di MSDN? Tak satu pun dari implementasi .NET sendiri yang memunculkan pengecualian. Melemparkan dalam kasus itu secara efektif akan mematahkan setiap upaya untuk menambah nullke HashSet<>. Kecuali HashSet<>melakukan sesuatu yang luar biasa ketika berhadapan dengan suatu nullitem (saya harus mengujinya).
EDIT / TAMBAHAN BARU:
Sekarang saya mencoba debugging. Dengan HashSet<>, saya dapat mengonfirmasi bahwa dengan pembanding kesetaraan default, nilai Season.Springdan null akan berakhir di keranjang yang sama. Ini dapat ditentukan dengan sangat hati-hati memeriksa anggota array pribadi m_bucketsdan m_slots. Perhatikan bahwa indeks selalu, menurut desain, diimbangi satu.
Kode yang saya berikan di atas tidak, bagaimanapun, memperbaiki ini. Ternyata, HashSet<>bahkan tidak akan pernah meminta pembanding kesetaraan ketika nilainya null. Ini dari kode sumber HashSet<>:
// Workaround Comparers that throw ArgumentNullException for GetHashCode(null).
private int InternalGetHashCode(T item) {
if (item == null) {
return 0;
}
return m_comparer.GetHashCode(item) & Lower31BitMask;
}
Artinya, setidaknya untuk HashSet<>, bahkan tidak mungkin untuk mengubah hash null. Sebagai gantinya, solusinya adalah mengubah hash dari semua nilai lainnya, seperti ini:
class NewerNullEnumEqComp<T> : EqualityComparer<T?> where T : struct
{
public override bool Equals(T? x, T? y)
{
return Default.Equals(x, y);
}
public override int GetHashCode(T? x)
{
return x.HasValue ? 1 + Default.GetHashCode(x) : /* not seen by HashSet: */ 0;
}
}