Bir set üyesi olarak System.Collections.Generic.HashSet<>
kabul etmek null
gibi koleksiyonlar göz önüne alındığında , hash kodunun ne null
olması gerektiği sorulabilir. Çerçeve 0
şunları kullanıyor gibi görünüyor :
// 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
Bu, null yapılabilir numaralandırmalarla (biraz) sorunlu olabilir. Eğer tanımlarsak
enum Season
{
Spring,
Summer,
Autumn,
Winter,
}
daha sonra Nullable<Season>
(aynı zamanda denir Season?
) yalnızca beş değer alabilir, ancak bunlardan ikisi, yani null
ve Season.Spring
aynı hash koduna sahiptir.
Bunun gibi "daha iyi" bir eşitlik karşılaştırıcısı yazmak cazip geliyor:
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;
}
}
Ama hash kodunun null
olması için herhangi bir sebep var 0
mı?
DÜZENLEME / EKLEME:
Bazı insanlar bunun ağır basmakla ilgili olduğunu düşünüyor Object.GetHashCode()
. Aslında değil. (.NET yazarları geçersiz kılmasını yaptınız mı GetHashCode()
içinde Nullable<>
yapı olduğu olsa alakalı.) Parametresiz bir kullanıcı yazılı uygulanmasını GetHashCode()
kimin hash kodu aradığımız nesne olduğu durumu idare asla null
.
Bu, soyut yöntemin EqualityComparer<T>.GetHashCode(T)
uygulanması veya başka bir şekilde arayüz yönteminin uygulanmasıyla ilgilidir IEqualityComparer<T>.GetHashCode(T)
. Şimdi, MSDN'ye bu bağlantıları oluştururken, orada bu yöntemlerin ArgumentNullException
yegane argümanları ise bir attığını söylediğini görüyorum null
. Bu kesinlikle MSDN'de bir hata olmalı? .NET'in kendi uygulamalarından hiçbiri özel durum oluşturmaz. Bu durumda fırlatma, null
bir HashSet<>
. Sürece HashSet<>
bir uğraşırken olağanüstü bir şey yapar null
öğe (Testten o gerekecektir).
YENİ DÜZENLEME / EKLEME:
Şimdi hata ayıklamayı denedim. İle HashSet<>
, ben varsayılan eşitlik karşılaştırıcısı ile değerlerini teyit edebiliriz Season.Spring
ve null
olacak aynı kovada sonunda. Bu, özel dizi üyelerini çok dikkatli bir şekilde inceleyerek belirlenebilir m_buckets
ve m_slots
. Endekslerin her zaman tasarım gereği birer birer kaydırıldığına dikkat edin.
Ancak yukarıda verdiğim kod bunu düzeltmiyor. Görünüşe göre HashSet<>
, değerin ne zaman olduğu eşitlik karşılaştırıcısına asla sormayacak null
. Bu, kaynak kodundan alınmıştır 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;
}
Bu, en azından için HashSet<>
hash değerini değiştirmenin bile mümkün olmadığınull
anlamına gelir . Bunun yerine, bir çözüm, diğer tüm değerlerin karmasını şu şekilde değiştirmektir:
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;
}
}