Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 7개 있습니다.)
.NET Framework: 179. Dictionary.Get(A) 대신 Dictionary.Get(A.GetHashCode())를 사용해서는 안 되는 이유
; https://www.sysnet.pe.kr/2/0/889

.NET Framework: 295. 괜찮은 문자열 해시 함수?
; https://www.sysnet.pe.kr/2/0/1222

.NET Framework: 296. 괜찮은 문자열 해시함수? - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/1223

개발 환경 구성: 478. 파일의 (sha256 등의) 해시 값(checksum) 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12176

.NET Framework: 1070. 사용자 정의 GetHashCode 메서드 구현은 C# 9.0의 record 또는 리팩터링에 맡기세요.
; https://www.sysnet.pe.kr/2/0/12669

.NET Framework: 2047. Golang, Python, C#에서의 CRC32 사용
; https://www.sysnet.pe.kr/2/0/13124

닷넷: 2371. C# - CRC64 (System.IO.Hashing의 약식 버전)
; https://www.sysnet.pe.kr/2/0/14027




사용자 정의 GetHashCode 메서드 구현은 C# 9.0의 record 또는 리팩터링에 맡기세요.

GetHashCode 코드 질문이 종종 올라오니,

GethashCode와 String대한 질문
; https://www.sysnet.pe.kr/3/0/5514

GetHashCode 질문있습니다!
; https://www.sysnet.pe.kr/3/0/5480

간단하게 팁 정도로 공유해서 전달해 드리는 것이 좋을 듯해서 글을 써봅니다. ^^

보통, 닷넷에서 GetHashCode 메서드를 이용해 hash 값을 구하긴 해도 막상 우리가 만든 타입에서 GetHashCode를 작성하려고 하면 코드 구현에서 고민이 됩니다. 가령 다음과 같은 예제가 있을 때,

public class Person
{
    public string Name;
    public int Age;

    public override int GetHashCode()
    {
        // ... hashcode 계산 ...
    }
}

과연 저 값을 어떻게 계산해야 할지 고민이 될 것입니다. 이럴 때는, 그냥 마이크로소프트가 하는 방법을 따르는 것도 좋습니다. 이를 위해 동일한 타입을 C# 9.0의 record로,

C# 9.0 - (9) 레코드(Records)
; https://www.sysnet.pe.kr/2/0/12392

만들면,

public record Person2
{
    public string Name;
    public int Age;
}

빌드 결과물로부터 역어셈블러를 통해 다음의 결과를 얻을 수 있습니다.

public override int GetHashCode()
{
    return (EqualityComparer<Type>.Default.GetHashCode(this.EqualityContract) * -1521134295
     + EqualityComparer<string>.Default.GetHashCode(this.Name)) * -1521134295
     + EqualityComparer<int>.Default.GetHashCode(this.Age);
}

음... 별다른 양심의 거리낌 없이 ^^ 복사해서 쓰면 됩니다. 만약 컬렉션 내에 같은 타입끼리만 있다면 다음과 같은 식으로 간략화해 처리해도 무방합니다.

public class Person
{
    public string Name;
    public int Age;

    public override int GetHashCode()
    {
        return EqualityComparer<string>.Default.GetHashCode(this.Name)) * -1521134295
             + EqualityComparer<int>.Default.GetHashCode(this.Age);
    }
}

혹은 이렇게 단순화해도 좋을 듯 싶고.

public override int GetHashCode()
{
    return this.Name.GetHashCode() * -1521134295
            + this.Age.GetHashCode();
}




또는, Visual Studio를 사용하신다면 우 클릭을 해 "Quick Actions and Refactorings..." 메뉴를 불러,

cs_gethascode_1.png

선택하면 다음과 같이 "Generate Equals and GetHashCode..." 기능을 선택할 수 있습니다.

cs_gethascode_2.png

그럼 hash 값을 구할 멤버를 선택하는 대화창이 뜨고,

cs_gethascode_3.png

적절한 설정 후 "OK" 버튼을 누르면 다음과 같이 알아서 GetHashCode를 만들어 줍니다.

// .NET Core 프로젝트

public class Person
{
    public string Name;
    public int Age;

    public override bool Equals(object obj)
    {
        return obj is Person person &&
               Name == person.Name &&
               Age == person.Age;
    }

    public override int GetHashCode()
    {
        // GetHashCode() in .NET Core
        // https://bartwullems.blogspot.com/2024/01/gethashcode-in-net-core.html
        return HashCode.Combine(Name, Age);
    }

    /* 또는, https://montemagno.com/optimizing-c-struct-equality-with-iequatable/

    public bool Equals(Person other) => (Name, Age) == (other.Name, other.Age);

    public override int GetHashCode() => (Name, Age).GetHashCode();
    */
}

.NET Core 프로젝트부터 HashCode.Combine이 사용되며 .NET Framework 프로젝트에서는 다음과 같이 record에서와 유사한 코드가 생성됩니다.

// .NET Framework 프로젝트

public class Person
{
    public string Name;
    public int Age;

    public override bool Equals(object obj)
    {
        return obj is Person person &&
               Name == person.Name &&
               Age == person.Age;
    }

    public override int GetHashCode()
    {
        int hashCode = -1360180430;
        hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
        hashCode = hashCode * -1521134295 + Age.GetHashCode();
        return hashCode;
    }
}




다시 한번 말씀드리면, 어차피 4바이트 정숫값으로는 충돌을 피할 수 없으므로 GetHashCode에 많은 고민을 하실 필요는 없습니다. 단지, 충돌에 대비해 Equals만 제대로 정의하면 BCL 자료 구조 내에서의 동작에는 문제가 없습니다.

물론, 성능에 아주/엄청나게 민감한 응용 프로그램이라면 최대한 저 메서드를 능력껏 간소화시키시면 됩니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/2/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 196  197  198  199  200  [201]  202 
NoWriterDateCnt.TitleFile(s)
30정성태7/17/200342305VC++: 7. [STL] vector 사용법 및 reference 사용예 [1]파일 다운로드1
28정성태7/17/200326504스크립트: 3. Programming Microsoft Internet Explorer 5 - CHM 파일
29정성태7/17/200326048    답변글 스크립트: 3.1. Programming Microsoft Internet Explorer 5 - 소스코드
27정성태7/17/200324909COM 개체 관련: 7. HTML Control에서 DELETE, 화살표 키 등이 안 먹는 문제
26정성태7/17/200325789COM 개체 관련: 6. WebBrowser 콘트롤에서 프레임을 구하는 소스
25정성태7/17/200323501COM 개체 관련: 5. C++ Attributes - Make COM Programming a Breeze with New Feature in Visual Studio .NET [2]파일 다운로드1
24정성태7/17/200327378.NET Framework: 5. (MHT 변환해서 가져온 글) .NET 의 COM+ 서비스 사용파일 다운로드1
23정성태7/17/200330894.NET Framework: 4. webservice.htc - HTML Script에서도 웹서비스 엑세스 [2]파일 다운로드1
22정성태7/17/200325314.NET Framework: 3. .NET Framework SDK 퀵 스타트 자습서
21정성태7/17/200324520.NET Framework: 2. 김현승님의 "ASP.NET & .NET EnterpriseServices & Remoting 코드 템플릿"
20정성태2/15/200532059VS.NET IDE: 2. Platform SDK 설치
19정성태7/17/200327671.NET Framework: 1. JScript.NET 강좌 사이트[영문]
18정성태7/17/200324900COM 개체 관련: 4. Exchanging Data Over the Internet Using XML [1]파일 다운로드1
17정성태7/17/200332701VC++: 6. Win32 API Hook - 소스는 "공개소스"에있습니다. [2]
16정성태7/17/200325417COM 개체 관련: 3. IE 툴밴드의 위치문제파일 다운로드1
15정성태7/17/200326275VC++: 5. 시행착오 - 클래스 포인터를 void * 로 대입후 delete 하는 경우.
14정성태7/17/200327966VC++: 4. MFC Message 처리 구조
13정성태7/17/200327700VC++: 3. template 활용의 최고 단계!
12정성태5/7/200625497VC++: 2. void func1( MYCLASS *&pBuildingElement ); 선언의 의미
10정성태7/17/200323722기타: 2. 데브피아 사이트의 클럽 서비스 내에 있는 Standard C++ Research
9정성태7/17/200329127기타: 1. Programming Applications for Microsoft Windows 4th Edition
8정성태7/17/200326497COM 개체 관련: 2. CWindowImpl 의 기본 윈도우 클래스 명을 바꾸려면?
7정성태7/17/200329123VS.NET IDE: 1. VC++ 프로파일링 사용법
4정성태7/17/200341960VC++: 1. C++ 클래스 멤버변수 초기화 [3]
3정성태7/17/200327718스크립트: 2. JScript에서의 Blocking 동작을 막아주는 COM 메서드
2정성태6/14/200646144COM 개체 관련: 1. IWebBrowser2와 IHTMLDocument2의 상호 변환 [2]
... 196  197  198  199  200  [201]  202