집합 멤버로 System.Collections.Generic.HashSet<>
accept 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
이것은 nullable 열거 형에서 (약간) 문제가 될 수 있습니다. 우리가 정의한다면
enum Season
{
Spring,
Summer,
Autumn,
Winter,
}
다음은 Nullable<Season>
(또한 Season?
), 즉 불과 5 값,하지만 그들 중 두 가지를 취할 수 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
A를을 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;
}
}