System.Collections.Generic.HashSet<>
セットメンバーとして受け入れるようなコレクションを考えると、のハッシュコードはどうあるべきnull
かを尋ねることができます。null
フレームワークが使用しているよう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
これは、null許容の列挙型では(少し)問題になる可能性があります。定義すると
enum Season
{
Spring,
Summer,
Autumn,
Winter,
}
その場合、Nullable<Season>
(とも呼ばれSeason?
ます)は5つの値を取ることができますが、そのうちの2つ、つまりnull
とSeason.Spring
は同じハッシュコードを持ちます。
次のような「より優れた」等式比較器を作成するのは魅力的です。
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;
}
}
null
しかし、のハッシュコードが必要な理由はあります0
か?
編集/追加:
一部の人々は、これがオーバーライドについてであると考えているようですObject.GetHashCode()
。実際にはそうではありません。(ただし、.NETの作成者は、関連GetHashCode()
するNullable<>
構造体でオーバーライドを行いました。)パラメーターなしのユーザー作成の実装では、ハッシュコードが。であるオブジェクトの状況を処理できません。GetHashCode()
null
これは、abstractメソッドのEqualityComparer<T>.GetHashCode(T)
実装、またはその他の方法でinterfaceメソッドの実装に関するものIEqualityComparer<T>.GetHashCode(T)
です。さて、MSDNへのこれらのリンクを作成している間、これらのメソッドArgumentNullException
が唯一の引数が。である場合にこれらのメソッドがスローすることを示していることがわかりますnull
。これは確かにMSDNの間違いであるに違いありませんか?.NET独自の実装はいずれも例外をスローしません。その場合にスローすると、に追加しようとする試みが事実上中断null
されHashSet<>
ます。アイテムHashSet<>
を扱うときに特別なことをしない限り(私はそれをテストする必要があります)。null
新しい編集/追加:
今、私はデバッグを試みました。を使用HashSet<>
すると、デフォルトの等式比較器を使用して、値Season.Spring
とnull
が同じバケットで終了することを確認できます。m_buckets
これは、プライベート配列メンバーとを非常に注意深く調べることで判断できますm_slots
。インデックスは、設計上、常に1つオフセットされていることに注意してください。
ただし、上記のコードでは修正されていません。結局のところ、値がHashSet<>
である場合、等式比較器に尋ねることさえありませんnull
。これは次のソースコードからのものです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;
}
これは、少なくともHashSet<>
、のハッシュを変更することさえできないことを意味しnull
ます。代わりに、解決策は、次のように、他のすべての値のハッシュを変更することです。
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;
}
}