セットメンバーとして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
。であるオブジェクトの状況を処理することはできません。
これは、抽象メソッドのEqualityComparer<T>.GetHashCode(T)
実装、またはその他の方法でインターフェイスメソッドの実装に関するもの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;
}
}