C #에 구조가 있습니다.
public struct UserInfo
{
public string str1
{
get;
set;
}
public string str2
{
get;
set;
}
}
유일한 규칙은 UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))
이 구조에 대해 GetHashCode 함수를 재정의하는 방법은 무엇입니까?
MSDN :
해시 함수에는 다음 속성이 있어야합니다.
- 두 객체가 동일하게 비교되는 경우 각 객체에 대한
GetHashCode
메서드는 동일한 값을 반환해야합니다. 그러나 두 개체가 동일하게 비교되지 않으면 두 개체의GetHashCode
메서드가 다른 값을 반환하지 않아도됩니다.- 객체의
GetHashCode
메서드의 반환 값을 결정하는 객체 상태가 수정되지 않는 한 객체의Equals
메서드는 동일한 해시 코드를 일관되게 반환해야합니다. 이는 현재 응용 프로그램 실행에 대해서만 적용되며 응용 프로그램을 다시 실행하면 다른 해시 코드가 반환 될 수 있습니다.- 최상의 성능을 얻으려면 해시 함수가 모든 입력에 대해 무작위 분포를 생성해야합니다.
올바른 방법을 고려하면 다음과 같습니다.
return str1.GetHashCode() ^ str2.GetHashCode()
^
는 다른 정류 연산으로 대체 될 수 있습니다.
Jon Skeet의 답변 -^
는 좋지 않습니다. 종종 충돌 해시를 생성합니다!
public override int GetHashCode()
{
unchecked
{
return (str1 ?? String.Empty).GetHashCode() +
(str2 ?? String.Empty).GetHashCode();
}
}
명시 적으로 ( 'AA', 'BB') 및 ( 'BB', 'AA')를 명시 적으로 동일하게 원하지만 '+'연산자를 사용하는 것이 '^'를 사용하는 것보다 낫습니다. 'AA', 'AA') 및 ( 'BB', 'BB')는 동일해야합니다 (또는 해당 문제에 대해 모두 동일한 쌍).
Null의 경우 빈 문자열에서 알려진 상수를 즉시 반환하지 않고 'GetHashCode ()'를 수행하기 때문에 '가능한 한 빨리'규칙을 완전히 준수하지는 않지만 명시 적으로 측정하지 않아도 널을 많이 기대하지 않으면 그 차이가 걱정할 정도로 크지 않을 것이라는 추측을 위험에 빠뜨리기 위해.
일반적으로 클래스에 대한 해시 코드를 생성하는 간단한 방법은 XOR 해시 코드 생성에 참여할 수있는 모든 데이터 필드입니다. UserInfo ( "AA", "BB") 및 UserInfo ( "BB", "AA")의 해시 코드가 동일해야한다는 (인공적?) 요구 사항도 충족합니다.
클래스 사용에 대한 가정을 할 수 있다면 해시 함수를 향상시킬 수 있습니다. 예를 들어, str1과 str2가 동일한 것이 일반적인 경우 XOR은 좋은 선택이 아닐 수 있습니다. 그러나 str1과 str2가 이름과 성을 나타내는 경우 XOR는 아마도 좋은 선택 일 것입니다.
비록 이것이 실제적인 예는 아니지만, 다음과 같은 점을 지적 할 가치가 있습니다 :-이것은 아마도 구조체의 사용에 대한 좋지 않은 예일 것입니다 : 구조체는 일반적으로 가치 의미론을 가져야합니다. 여기에 사건. -setter와 함께 속성을 사용하여 해시 코드를 생성하는 것도 문제를 요구합니다.
간단한 general 방법은 다음과 같습니다.
return string.Format("{0}/{1}", str1, str2).GetHashCode();
엄격한 성능 요구 사항이 없으면 이것이 내가 생각할 수있는 가장 쉬운 방법이며 복합 키가 필요할 때이 방법을 자주 사용합니다. 그것은 null
경우를 잘 처리하고 (m) 해시 충돌을 일으키지 않습니다 (일반적으로). 문자열에 '/'가 필요하면 예상하지 못한 다른 구분 기호를 선택하십시오.
ReSharper가 제안하는 내용은 다음과 같습니다.
public int GetHashCode()
{
unchecked
{
int hashCode;
// String properties
hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0);
// int properties
hashCode = (hashCode * 397) ^ intProperty;
return hashCode;
}
}
397은 결과 변수가 오버플로되어 해시 비트를 약간 혼합하여 해시 코드를 더 잘 분배 할 수있는 충분한 크기의 소수입니다. 그렇지 않으면 397에는 같은 크기의 다른 소수와 구별되는 특별한 것이 없습니다.
public override int GetHashCode()
{
unchecked
{
return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0);
}
}
Gary Shutler가 지적했듯이 아.
return str1.GetHashCode() + str2.GetHashCode();
넘칠 수 있습니다. Artem이 제안한대로 캐스팅을 시도하거나 선택하지 않은 키워드로 명령문을 둘러 쌀 수 있습니다.
return unchecked(str1.GetHashCode() + str2.GetHashCode());
이것을 시도하십시오 :
(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()
많은 가능성. 예 :.
return str1.GetHashCode() ^ str1.GetHashCode()
아마도 str1.GetHashCode () + str2.GetHashCode ()와 같은 것입니까? 또는 (str1.GetHashCode () + str2.GetHashCode ())/2? 이렇게하면 str1과 str2가 바뀌 었는지 여부에 관계없이 동일합니다 ...
정렬 한 다음 연결하십시오.
반환 ((str1.CompareTo (str2) <1)? str1 + str2 : str2 + str1) .GetHashCode ();
GetHashCode의 결과는 다음과 같습니다.
사람들을 염두에두고 다음과 같이 갈 것입니다.
if (str1 == null)
if (str2 == null)
return 0;
else
return str2.GetHashCode();
else
if (str2 == null)
return str1.GetHashCode();
else
return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();
편집 : 널을 잊어 버렸습니다. 코드가 수정되었습니다.