Angesichts der Tatsache, dass Sammlungen gerne als festgelegtes Mitglied System.Collections.Generic.HashSet<>akzeptieren null, kann man sich fragen, wie der Hash-Code nulllauten 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 nullund 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 nullsein 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 ArgumentNullExceptionif 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, nullzu a hinzuzufügen HashSet<>. Es sei denn, HashSet<>es handelt sich um etwas Außergewöhnliches, wenn es um einen nullGegenstand 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.Springund null werde in dem gleichen Eimer beenden. Dies kann durch sehr sorgfältige Prüfung der privaten Array-Mitglieder m_bucketsund 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;
}
}