C #의 GetHashCode 지침
Essential C # 3.0 및 .NET 3.5 책에서 다음을 읽었습니다.
개체의 데이터가 변경 되더라도 특정 개체의 수명 동안 GetHashCode ()의 반환 값은 일정해야합니다 (동일한 값). 대부분의 경우이를 적용하기 위해 메소드 리턴을 캐시해야합니다.
이것이 유효한 지침입니까?
.NET에서 몇 가지 기본 제공 유형을 시도했지만 이러한 방식으로 작동하지 않았습니다.
대답은 대부분 유효한 지침이지만 유효한 규칙은 아닙니다. 그것은 또한 전체 이야기를 말하지 않습니다.
요점은 가변 유형의 경우 두 개의 동일한 객체가 동일한 해시 코드를 반환해야하며 해시 코드가 객체의 수명 기간 동안 유효해야하기 때문에 가변 데이터를 기반으로 해시 코드를 만들 수 없다는 것입니다. 해시 코드가 변경되면 올바른 해시 저장소에 더 이상 존재하지 않기 때문에 해시 컬렉션에서 손실되는 개체가 생깁니다.
예를 들어 객체 A는 해시 1을 반환하므로 해시 테이블의 빈 1에 들어갑니다. 그런 다음 해시 테이블 2를 반환하도록 객체 A를 변경합니다. 해시 테이블을 찾으면 빈 2를 찾고 찾을 수 없습니다. 객체가 빈 1에서 분리 된 것입니다.
객체의 수명 기간 동안
변경되지 않으며
GetHashCode 구현을 작성하는 것이 한 가지 이유입니다.
에릭 리퍼 업데이트에 대한 훌륭한 정보를 제공 하는 블로그 를 게시했습니다GetHashCode
.
추가 업데이트
위의 몇 가지 사항을 변경했습니다.
- 나는 지침과 규칙을 구분했다.
- 나는 "개체의 수명 동안"을 쳤다.
가이드 라인은 규칙이 아니라 가이드 일뿐입니다. 실제로, GetHashCode
객체가 해시 테이블에 저장 될 때와 같이 객체가 지침을 따를 것으로 예상하는 경우에만이 지침을 따라야합니다. 해시 테이블 (또는의 규칙에 의존하는 것)에서 객체를 사용하지 않으려는 경우 GetHashCode
구현시 지침을 따를 필요가 없습니다.
"개체의 수명 동안"이 표시되면 "개체가 해시 테이블과 협력해야하는 시간"또는 이와 유사한 내용을 읽어야합니다. 대부분의 경우와 마찬가지로 GetHashCode
규칙을 어기는시기를 아는 것 입니다.
오랜 시간이 지났지 만 그럼에도 불구하고 이유와 방법에 대한 설명을 포함 하여이 질문에 대한 올바른 대답을 제시해야한다고 생각합니다. 지금까지 가장 좋은 대답은 MSDN을 철저하게 인용 한 것입니다. 자신의 규칙을 만들려고하지 마십시오. MS 직원은 자신이하는 일을 알고있었습니다.
그러나 가장 먼저해야 할 일 : 질문에 인용 된 지침이 잘못되었습니다.
이제 그 이유는 두 가지입니다
첫 번째 이유 : 해시 코드가 계산 된 방식으로, 객체 자체가 변경 되더라도 등식 계약을 위반하는 것보다 객체 수명 기간 동안 변경되지 않습니다.
기억하십시오 : "두 개체가 동일하게 비교되면 각 개체에 대한 GetHashCode 메소드는 동일한 값을 리턴해야합니다. 그러나 두 개체가 동일하게 비교되지 않으면 두 오브젝트에 대한 GetHashCode 메소드는 다른 값을 리턴하지 않아도됩니다."
두 번째 문장은 종종 "객체 생성시 동일한 객체의 해시 코드가 동일해야한다는 유일한 규칙"으로 잘못 해석됩니다. 이유를 모릅니다. 그러나 그것은 대부분의 대답의 본질입니다.
equals 메소드에서 이름이 사용되는 이름을 포함하는 두 개의 오브젝트를 생각해보십시오. 같은 이름-> 같은 것. 인스턴스 A 만들기 : 이름 = Joe 인스턴스 B 만들기 : 이름 = Peter
해시 코드 A와 해시 코드 B는 동일하지 않을 가능성이 높습니다. 인스턴스 B의 이름이 Joe로 변경되면 이제 어떻게됩니까?
질문의 지침에 따르면 B의 해시 코드는 변경되지 않습니다. 결과는 다음과 같습니다. A.Equals (B) ==> true 그러나 동시에 : A.GetHashCode () == B.GetHashCode () ==> false.
그러나 정확히 equals & hashcode-contract에 의해이 동작이 명시 적으로 금지됩니다.
두 번째 이유 : 물론-해시 코드의 변경으로 인해 해시 코드를 사용하여 해시 목록 및 기타 객체가 손상 될 수 있지만 그 반대도 마찬가지입니다. 해시 코드를 변경하지 않으면 최악의 경우 해시 목록을 얻습니다. 여기서 많은 다른 객체는 모두 동일한 해시 코드를 가지므로 동일한 해시 빈에 있습니다-예를 들어 객체가 표준 값으로 초기화 될 때 발생합니다.
글쎄요, 언뜻보기에 모순이있는 것처럼 보입니다. 어쨌든 코드가 깨질 것입니다. 그러나 변경되거나 변경되지 않은 해시 코드에서 발생하는 문제는 없습니다.
문제의 원인은 MSDN에 잘 설명되어 있습니다.
MSDN의 해시 테이블 항목에서 :
키 오브젝트는 Hashtable에서 키로 사용되는 한 변경 불가능해야합니다.
이것은 다음을 의미합니다.
해시 값을 생성하는 모든 객체는 해시 값을 변경해야하지만 객체가 변경 될 때 해시 값을 변경해야하지만 Hashtable (또는 다른 Hash 사용 객체) 내부에서 사용될 때 자체 변경을 허용해서는 안됩니다. .
첫 번째 방법은 물론 해시 테이블에서만 사용하기 위해 불변 객체를 디자인하는 방법입니다. 이것은 필요할 때 일반 가변 객체의 사본으로 만들어집니다. 불변 객체 내부에서 해시 코드는 불변이기 때문에 해시 코드를 캐시하는 것이 좋습니다.
두 번째 방법 또는 객체에 "지금 해시되었습니다"플래그를 지정하고 모든 객체 데이터가 비공개인지 확인하고 객체 데이터를 변경할 수있는 모든 함수에서 플래그를 확인하고 변경이 허용되지 않는 경우 예외 데이터를 throw합니다 (예 : 플래그가 설정 됨) ). 이제 해시 영역에 객체를 넣을 때 플래그를 설정하고 더 이상 필요하지 않은 경우 플래그를 설정 해제하십시오. 사용하기 쉽도록 "GetHashCode"메서드 내에서 플래그를 자동으로 설정하는 것이 좋습니다.이 방법으로 잊을 수 없습니다. 그리고 "ResetHashFlag"메소드를 명시 적으로 호출하면 프로그래머가 지금까지 오브젝트 데이터를 변경할 수 있는지 여부를 생각해야합니다.
좋아, 말해야 할 사항 : 등가 및 해시 코드 계약을 위반하지 않고 객체 데이터가 변경 될 때 해시 코드가 변경되지 않는 가변 데이터가있는 객체를 가질 수있는 경우가 있습니다.
그러나 등식 방법이 변경 가능한 데이터를 기반으로하지 않아야합니다. 따라서 객체를 작성하고 값을 한 번만 계산하고 객체 내부에 저장하여 나중에 호출 할 때 값을 반환하는 GetHashCode 메소드를 작성하면 다시해야합니다. 절대적으로 Equals 메소드를 작성해야합니다. A.Equals (B)도 false에서 true로 변경되지 않도록 비교를 위해 저장된 값입니다. 그렇지 않으면 계약이 파기됩니다. 이것의 결과는 일반적으로 Equals 메소드가 의미가 없다는 것입니다. 원래 참조가 같지는 않지만 값도 같지 않습니다. 때로는 의도 된 동작 (예 : 고객 레코드) 일 수도 있지만 일반적으로 그렇지 않습니다.
So, just make GetHashCode result change, when the object data changes, and if the use of the object inside of hash using lists or objects is intended (or just possible) then make the object either immutable or create a readonly flag to use for the lifetime of a hashed list containing the object.
(By the way: All of this is not C# oder .NET specific - it is in the nature of all hashtable implementations, or more generally of any indexed list, that identifying data of objects should never change, while the object is in the list. Unexpected and unpredictable behaviour will occur, if this rule is broken. Somewhere, there may be list implementations, that do monitor all elements inside the list and do automatic reindexing the list - but the performance of those will surely be gruesome at best.)
From MSDN
If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two object do not have to return different values.
The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state that determines the return value of the object's Equals method. Note that this is true only for the current execution of an application, and that a different hash code can be returned if the application is run again.
For the best performance, a hash function must generate a random distribution for all input.
This means that if the value(s) of the object change, the hash code should change. For example, a "Person" class with the "Name" property set to "Tom" should have one hash code, and a different code if you change the name to "Jerry". Otherwise, Tom == Jerry, which is probably not what you would have intended.
Edit:
Also from MSDN:
Derived classes that override GetHashCode must also override Equals to guarantee that two objects considered equal have the same hash code; otherwise, the Hashtable type might not work correctly.
From MSDN's hashtable entry:
Key objects must be immutable as long as they are used as keys in the Hashtable.
The way I read this is that mutable objects should return different hashcodes as their values change, unless they are designed for use in a hashtable.
In the example of System.Drawing.Point, the object is mutable, and does return a different hashcode when the X or Y value changes. This would make it a poor candidate to be used as-is in a hashtable.
I think that the documentation regarding GetHashcode is a bit confusing.
On one hand, MSDN states that the hashcode of an object should never change , and be constant On the other hand, MSDN also states that the return value of GetHashcode should be equal for 2 objects, if those 2 objects are considered to be equal.
A hash function must have the following properties:
- If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two object do not have to return different values.
- The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state that determines the return value of the object's Equals method. Note that this is true only for the current execution of an application, and that a different hash code can be returned if the application is run again.
- For the best performance, a hash function must generate a random distribution for all input.
Then, this means that all your objects should be immutable, or the GetHashcode method should be based on properties of your object that are immutable. Suppose for instance that you have this class (naive implementation):
public class SomeThing
{
public string Name {get; set;}
public override GetHashCode()
{
return Name.GetHashcode();
}
public override Equals(object other)
{
SomeThing = other as Something;
if( other == null ) return false;
return this.Name == other.Name;
}
}
This implementation already violates the rules that can be found in MSDN. Suppose you have 2 instances of this class; the Name property of instance1 is set to 'Pol', and the Name property of instance2 is set to 'Piet'. Both instances return a different hashcode, and they're also not equal. Now, suppose that I change the Name of instance2 to 'Pol', then, according to my Equals method, both instances should be equal, and according to one of the rules of MSDN, they should return the same hashcode.
However, this cannot be done, since the hashcode of instance2 will change, and MSDN states that this is not allowed.
Then, if you have an entity, you could maybe implement the hashcode so that it uses the 'primary identifier' of that entity, which is maybe ideally a surrogate key, or an immutable property. If you have a value object, you can implement the Hashcode so that it uses the 'properties' of that value object. Those properties make up the 'definition' of the value object. This is of course the nature of a value object; you're not interested in it's identity, but rather in it's value.
And, therefore, value objects should be immutable. (Just like they are in the .NET framework, string, Date, etc... are all immutable objects).
Another thing that comes in mind:
During which 'session' (I don't know really how I should call this) should 'GetHashCode' return a constant value. Suppose you open up your application, load an instance of an object out of the DB (an entity), and get its hashcode. It will return a certain number. Close the application, and load the same entity. Is it required that the hashcode this time has the same value as when you loaded the entity the first time ? IMHO, not.
This is good advice. Here's what Brian Pepin has to say on the matter:
This has tripped me up more than once: Make sure GetHashCode always returns the same value across the lifetime of an instance. Remember that hash codes are used to identify "buckets" in most hashtable implementations. If an object's "bucket" changes, a hashtable may not be able to find your object. These can be very hard bugs to find, so get it right the first time.
Not directly answering your question, but - if you use Resharper, do not forget it has a feature that generates a reasonable GetHashCode implementation (as well as the Equals method) for you. You can of course specify which members of the class will be taken into account when computing the hashcode.
Check out this blog post from Marc Brooks:
VTOs, RTOs and GetHashCode() -- oh, my!
And then check out the follow up post (can't link as I'm new, but there's a link in the initlal article) which discusses further and covers some minor weaknesses in the initial implementation.
This was everything I needed to know about creating a GetHashCode() implementation, he even provides a download of his method along with some other utilities, in short gold.
The hashcode never changes, but it's also important to understand where the Hashcode is coming from.
If your object is using value semantics, i.e. the object's identity is defined by its values (like String, Color, all structs). If your object's identity is independent of all of its values, then the Hashcode is identified by a subset of its values. For example, your StackOverflow entry is stored in a database somewhere. If you change your name or email, your customer entry stays the same, although some values have changed (ultimately you're usually identified by some long customer id #).
So in short:
Value type semantics - Hashcode is defined by values Reference type semantics - Hashcode is defined by some id
I suggest you read Domain Driven Design by Eric Evans, where he goes into entities vs value types (which is more or less what I attempted to do above) if this still doesn't make sense.
Check out Guidelines and rules for GetHashCode by Eric Lippert
참고URL : https://stackoverflow.com/questions/462451/gethashcode-guidelines-in-c-sharp
'development' 카테고리의 다른 글
Enterprise Library Unity 및 기타 IoC 컨테이너 (0) | 2020.06.28 |
---|---|
PDO 데이터베이스 쿼리를 디버깅하는 방법? (0) | 2020.06.28 |
Visual Studio 2012-Intellisense가 때때로 사라지거나 손상됨 (0) | 2020.06.28 |
OS X에서 터미널을 사용하여 디렉토리를 이동하는 방법 (0) | 2020.06.28 |
A와 A가 없음이 아닌 경우 : (0) | 2020.06.28 |