Bir set üyesi olarak System.Collections.Generic.HashSet<>kabul etmek nullgibi koleksiyonlar göz önüne alındığında , hash kodunun ne nullolması 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 nullve Season.Springaynı 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 nullolması için herhangi bir sebep var 0mı?
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 ArgumentNullExceptionyegane 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, nullbir 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.Springve null olacak aynı kovada sonunda. Bu, özel dizi üyelerini çok dikkatli bir şekilde inceleyerek belirlenebilir m_bucketsve 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;
}
}