변경 가능한 객체에 대해 GetHashCode를 재정의 하시겠습니까?
재정의시기와 방법에 대한 10 개의 다른 질문을 읽었 GetHashCode
지만 여전히 이해하지 못하는 것이 있습니다. 의 대부분의 구현은 GetHashCode
객체 필드의 해시 코드를 기반으로하지만 객체 GetHashCode
의 수명 동안 값이 변경되어서 는 안된다고 언급되었습니다 . 기반 필드가 변경 가능한 경우 어떻게 작동합니까? 또한 딕셔너리 조회 등이 재정의되지 않은 참조 평등을 기반으로하려면 Equals
어떻게해야합니까?
나는 주로 Equals
직렬화 코드를 직렬화 및 역 직렬화 (내 경우 XML로)한다고 가정하는 단위 테스트의 용이성을 위해 우선적으로 재정의 하고 있으므로 적어도 값 동등성에 의해 올바른지 확인하고 싶습니다. Equals
이 경우 재정의하는 것이 나쁜 습관 입니까? 기본적으로 대부분의 실행 코드에서 참조 평등을 원하며 항상 사용 ==
하며 재정의하지 않습니다. ValueEquals
재정의하는 대신 새 메서드 또는 무언가를 만들어야 Equals
합니까? 나는 프레임 워크가 항상 사용 ==
하고 Equals
비교 하지 않는다고 가정하곤했다. 그래서 나는 Equals
그것의 목적이 당신이 다른 것과 다른 평등에 대한 2 차 정의를 원한다면 그 목적인 것처럼 보였기 때문에 재정의하는 것이 안전하다고 생각했다 .==
운영자. 다른 몇 가지 질문을 읽은 것은 사실이 아닌 것 같습니다.
편집하다:
내 의도가 명확하지 않은 것 같습니다. 즉, 99 %의 시간 동안 평범한 참조 평등, 기본 동작, 놀라움이 필요하지 않습니다. 매우 드문 경우에 대해 값 평등을 원하고 .Equals
대신을 사용하여 값 평등을 명시 적으로 요청하고 싶습니다 ==
.
이 작업을 수행 할 때 컴파일러는 재정의 GetHashCode
할 것을 권장하며 이것이이 질문이 나온 방법입니다. GetHashCode
변경 가능한 객체에 적용될 때 모순되는 목표가있는 것처럼 보였습니다 .
- 만약
a.Equals(b)
다음a.GetHashCode()
해야한다== b.GetHashCode()
. - 의 값은
a.GetHashCode()
의 수명 동안 변경되지 않아야합니다a
.
객체의 상태가 변경되면의 값이 .Equals()
변경 될 것으로 예상하기 때문에 변경 가능한 객체가 변경되면 자연스럽게 모순되는 것 같습니다. 즉, GetHashCode
의 변경 사항과 일치하도록 변경 해야 .Equals()
하지만 GetHashCode
변경해서는 안됩니다.
왜 이런 모순이있는 것 같습니까? 이러한 권장 사항은 변경 가능한 객체에 적용되지 않습니까? 아마도 가정하지만 구조체가 아닌 클래스를 언급하고 있음을 언급 할 가치가 있습니다.
해결:
저는 JaredPar를 수락 된 것으로 표시하고 있지만 주로 댓글 상호 작용을 위해 사용합니다. 여기서 배운 것을 요약하면 모든 목표를 달성하고 엣지 케이스에서 가능한 기발한 동작을 피하는 유일한 방법은 변경 불가능한 필드를 재정의 Equals
하고 GetHashCode
기반으로하거나 IEquatable
. 이런 종류의 Equals
참조 유형에 대한 재정의의 유용성은 대부분의 참조 유형이 기본 키로 식별하기 위해 관계형 데이터베이스에 저장되지 않는 한 일반적으로 불변 필드가 없기 때문에 참조 유형에 대한 재정의의 유용성을 감소시키는 것 같습니다 .
기반 필드가 변경 가능한 경우 어떻게 작동합니까?
객체가 변경 될 때 해시 코드가 변경된다는 의미가 아닙니다. 그것은 당신이 읽은 기사에 나열된 모든 이유로 문제입니다. 불행히도 이것은 일반적으로 코너 케이스에서만 나타나는 문제 유형입니다. 따라서 개발자는 나쁜 행동을 피하는 경향이 있습니다.
또한 딕셔너리 조회 등이 재정의 된 Equals가 아닌 참조 동등성을 기반으로하도록하려면 어떻게해야합니까?
이와 같은 인터페이스를 구현하는 IEquatable<T>
한 문제가되지 않습니다. 대부분의 사전 구현은 IEquatable<T>
Object.ReferenceEquals 를 사용하는 방식으로 동등 비교자를 선택합니다 . 를 사용하지 않더라도 IEquatable<T>
대부분은 기본적으로 Object.Equals ()를 호출하여 구현으로 이동합니다.
기본적으로 대부분의 실행 코드에서 참조 평등을 원하고 항상 ==를 사용하며 재정의하지 않습니다.
객체가 값이 같게 작동 할 것으로 예상하는 경우 == 및! =를 재정 의하여 모든 비교에 대해 값이 같도록해야합니다. 실제로 참조 동등성을 원하는 경우 사용자는 여전히 Object.ReferenceEquals를 사용할 수 있습니다.
나는 프레임 워크가 항상 ==를 사용하고 Equals가 아닌 것을 비교한다고 가정했습니다.
BCL이 사용하는 것은 시간이 지남에 따라 약간 변경되었습니다. 이제 동등성을 사용하는 대부분의 경우는 IEqualityComparer<T>
인스턴스 를 가져와 동등성을 위해 사용합니다. 하나가 지정되지 않은 경우 하나 EqualityComparer<T>.Default
를 찾는 데 사용 합니다. 최악의 경우 기본적으로 Object.Equals를 호출합니다.
변경 가능한 개체가있는 경우 실제로 사용할 수 없기 때문에 GetHashCode 메서드를 재정의하는 데 별 의미가 없습니다. 예를 들어 Dictionary
및 HashSet
컬렉션에서 각 항목을 버킷에 배치 하는 데 사용됩니다 . 컬렉션에서 키로 사용되는 동안 객체를 변경하면 해시 코드가 더 이상 객체가있는 버킷과 일치하지 않으므로 컬렉션이 제대로 작동하지 않고 객체를 다시 찾지 못할 수 있습니다.
조회 에서 클래스 의 GetHashCode
또는 Equals
메서드 를 사용하지 않도록하려면 .NET Framework IEqualityComparer
를 만들 때 대신 사용할 자체 구현을 제공 할 수 있습니다 Dictionary
.
이 Equals
메서드는 가치 평등을위한 것이므로 그렇게 구현하는 것은 잘못이 아닙니다.
와우, 실제로 하나의 여러 질문입니다 :-). 그래서 차례로 :
GetHashCode의 값은 개체의 수명 동안 절대 변경되지 않아야한다고 언급되었습니다. 기반 필드가 변경 가능한 경우 어떻게 작동합니까?
이 일반적인 조언은 객체를 HashTable / dictionary 등의 키로 사용하려는 경우를위한 것입니다. HashTables는 키를 저장하고 검색하는 방법을 결정하는 데 사용하기 때문에 일반적으로 변경되지 않는 해시가 필요합니다. 해시가 변경되면 HashTable이 더 이상 개체를 찾지 못할 것입니다.
Java의 Map 인터페이스 문서를 인용하려면 :
참고 : 변경 가능한 오브젝트가 맵 키로 사용되는 경우에는 세심한주의가 필요합니다. 객체가지도에서 키인 동안 등식 비교에 영향을주는 방식으로 객체의 값이 변경되면지도의 동작이 지정되지 않습니다.
일반적으로 어떤 종류의 변경 가능한 객체를 해시 테이블의 키로 사용하는 것은 좋지 않습니다. 해시 테이블에 키가 추가 된 후 키가 변경되면 어떤 일이 발생해야하는지조차 명확하지 않습니다. 해시 테이블이 이전 키 또는 새 키를 통해 저장된 객체를 반환해야합니까?
따라서 실제 조언은 : 변경 불가능한 객체 만 키로 사용하고 해시 코드가 변경되지 않도록하십시오 (객체가 변경 불가능한 경우 일반적으로 자동 임).
또한 딕셔너리 조회 등이 재정의 된 Equals가 아닌 참조 동등성을 기반으로하도록하려면 어떻게해야합니까?
글쎄, 그렇게 작동하는 사전 구현을 찾으십시오. 그러나 표준 라이브러리 사전은 hashcode & Equals를 사용하며이를 변경할 방법이 없습니다.
나는 직렬화 및 역 직렬화 (내 경우에는 XML로)가 참조 동등성을 죽인다고 가정하는 직렬화 코드를 쉽게 단위 테스트하기 위해 Equals를 재정의하고 있으므로 적어도 값 동등성에 의해 올바른지 확인하고 싶습니다. 이 경우 Equals를 재정의하는 것이 나쁜 습관입니까?
아니, 그게 완벽하게 받아 들여질 것이다. 그러나 이러한 객체는 변경 가능하므로 사전 / 해시 테이블의 키로 사용해서는 안됩니다. 위 참조.
여기서 기본 주제는 객체를 가장 고유하게 식별하는 방법입니다. 참조 무결성이 해당 프로세스에서 손실되기 때문에 중요한 직렬화 / 역 직렬화를 언급합니다.
짧은 대답은 개체가 그렇게하는 데 사용할 수있는 가장 작은 불변 필드 집합으로 고유하게 식별되어야한다는 것입니다. GetHashCode 및 Equals를 재정의 할 때 사용해야하는 필드입니다.
테스트를 위해 필요한 어설 션을 정의하는 것은 완벽하게 합리적입니다. 일반적으로 이들은 유형 자체에 정의되지 않고 테스트 스위트의 유틸리티 메소드로 정의됩니다. 아마도 TestSuite.AssertEquals (MyClass, MyClass)?
GetHashCode와 Equals는 함께 작동해야합니다. GetHashCode는 두 개체가 동일한 경우 동일한 값을 반환해야합니다. Equals는 두 개체에 동일한 해시 코드가있는 경우에만 true를 반환해야합니다. (두 객체가 같지 않을 수 있지만 동일한 해시 코드를 반환 할 수 있습니다). 이 주제를 정면으로 다루는 웹 페이지가 많이 있습니다.
I don't know about C#, being a relative noob to it, but in Java, if you override equals() you need to also override hashCode() to maintain the contract between them (and vice-versa)... And java also has the same catch 22; basically forcing you use immutable fields... But this is an issue only for classes which are used as a hash-key, and Java has alternate implementations for all hash-based collections... which maybe not as fast, but they do effecitely allow you to use a mutable object as a key... it's just (usually) frowned up as a "poor design".
And I feel the urge to point out that this fundamental problem is timeless... It's been around since Adam was a lad.
I've worked on fortran code which is older than I am (I'm 36) which breaks when a username is changed (like when a girl gets married, or divorced ;-) ... Thus is engineering, The adopted solution was: The GetHashCode "method" remembers the previously calculated hashCode, recalculates the hashCode (i.e. a virtual isDirty marker) and if the keyfields have changed it returns null. This causes the cache to delete the "dirty" user (by calling another GetPreviousHashCode) and then the cache returns null, causing the user to re-read from the database. An interesting and worthwhile hack; even if I do say so myself ;-)
I'll trade-off mutability (only desirable in corner cases) for O(1) access (desirable in all cases). Welcome to engineering; the land of the informed compromise.
Cheers. Keith.
ReferenceURL : https://stackoverflow.com/questions/873654/overriding-gethashcode-for-mutable-objects
'developer tip' 카테고리의 다른 글
Haskell에서 복잡한 상태 유지 (0) | 2020.12.26 |
---|---|
Android Java는 Java 8에서 람다 표현식을 지원합니까? (0) | 2020.12.26 |
실제 색상을 혼합하는 것과 같은 색상 혼합 알고리즘이 있습니까? (0) | 2020.12.26 |
Apache에서 Clojure 웹 응용 프로그램을 통합하는 방법 (0) | 2020.12.26 |
UIGesture를 뒤에있는 뷰로 전달 (0) | 2020.12.26 |