Angesichts der Tatsache, dass Sammlungen gerne als festgelegtes Mitglied System.Collections.Generic.HashSet<>
akzeptieren null
, kann man sich fragen, wie der Hash-Code null
lauten soll. Es sieht so aus, als würde das Framework Folgendes verwenden 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
Dies kann bei nullbaren Aufzählungen (ein wenig) problematisch sein. Wenn wir definieren
enum Season
{
Spring,
Summer,
Autumn,
Winter,
}
dann kann der Nullable<Season>
(auch genannt Season?
) nur fünf Werte annehmen, aber zwei davon, nämlich null
und Season.Spring
, haben den gleichen Hash-Code.
Es ist verlockend, einen "besseren" Gleichheitsvergleich wie diesen zu schreiben:
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;
}
}
Aber gibt es einen Grund, warum der Hash-Code von null
sein sollte 0
?
EDIT / ADDITION:
Einige Leute scheinen zu denken, dass es um das Überschreiben geht Object.GetHashCode()
. Das ist es wirklich nicht. (Die Autoren von .NET hat eine Überschreibung von machen GetHashCode()
in der Nullable<>
Struktur , die ist relevant, though.) Ein benutzer geschrieben Umsetzung des parameterlos GetHashCode()
kann nie mit der Situation , wo das Objekt , dessen Hash - Code , den wir suchen , ist null
.
Hier geht es darum, die abstrakte Methode zu EqualityComparer<T>.GetHashCode(T)
implementieren oder die Schnittstellenmethode auf andere Weise zu implementieren IEqualityComparer<T>.GetHashCode(T)
. Jetzt, während ich diese Links zu MSDN erstelle, sehe ich, dass dort steht, dass diese Methoden ein ArgumentNullException
if auslösen, wenn ihr einziges Argument ist null
. Dies muss sicherlich ein Fehler bei MSDN sein? Keine der .NET-eigenen Implementierungen löst Ausnahmen aus. Das Werfen in diesem Fall würde effektiv jeden Versuch unterbrechen, null
zu a hinzuzufügen HashSet<>
. Es sei denn, HashSet<>
es handelt sich um etwas Außergewöhnliches, wenn es um einen null
Gegenstand geht (das muss ich testen).
NEUE BEARBEITUNG / ERGÄNZUNG:
Jetzt habe ich versucht zu debuggen. Mit HashSet<>
, kann ich das mit dem Standardgleichheitsvergleich bestätigen, die Werte Season.Spring
und null
werde in dem gleichen Eimer beenden. Dies kann durch sehr sorgfältige Prüfung der privaten Array-Mitglieder m_buckets
und festgestellt werden m_slots
. Beachten Sie, dass die Indizes von Natur aus immer um eins versetzt sind.
Der oben angegebene Code behebt dies jedoch nicht. Wie sich herausstellt, HashSet<>
wird der Gleichheitsvergleicher niemals gefragt, wann der Wert ist null
. Dies ist aus dem Quellcode von 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;
}
Dies bedeutet, dass es zumindest für HashSet<>
nicht einmal möglich ist, den Hash von zu ändern null
. Stattdessen besteht eine Lösung darin, den Hash aller anderen Werte wie folgt zu ändern:
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;
}
}