2017-01-29 7 views
0

나는 다음과 같은 클래스가 :를 GetHashCode 휴식 코드

이 코드를 사용하는 경우와 ResourcePack의 인스턴스 멤버를 생성하면 더 이상 변하지 않는 단 하나의 장소가있다
public class ResourcePack 
{ 
    private int m_pirates; 
    private int m_islands; 
    private int m_enemy_drones; 
    private int m_enemy_pirates; 

    public ResourcePack() 
    { 
     this.m_pirates = 0; 
     this.m_islands = 0; 
     this.m_enemy_drones = 0; 
     this.m_enemy_pirates = 0; 
    } 

    public ResourcePack(ResourcePack other) 
    { 
     this.m_pirates = other.m_pirates; 
     this.m_islands = other.m_islands; 
     this.m_enemy_drones = other.m_enemy_drones; 
     this.m_enemy_pirates = other.m_enemy_pirates; 
    } 

    public bool CanConcatinate(ResourcePack other) 
    { 
     if ((this.m_islands & other.m_islands) != 0) 
     { 
      return false; 
     } 

     if ((this.m_enemy_drones & other.m_enemy_drones) != 0) 
     { 
      return false; 
     } 

     if ((this.m_enemy_pirates & other.m_enemy_pirates) != 0) 
     { 
      return false; 
     } 

     return true; 
    } 

    internal void AddIsland(int island) 
    { 
     m_islands |= (1 << island); 
    } 

    internal void AddPirate(int pirate) 
    { 
     m_pirates |= (1 << pirate); 
    } 

    internal void AddEnemyDrone(int drone) 
    { 
     m_enemy_drones |= (1 << drone); 
    } 

    internal void AddEnemyPirates(int pirate) 
    { 
     m_enemy_pirates |= (1 << pirate); 
    } 

    public ResourcePack SemiConcatinate(ResourcePack other) 
    { 
     var ret = new ResourcePack(); 

     ret.m_pirates  = this.m_pirates | other.m_pirates; 
     ret.m_islands  = this.m_islands | other.m_islands; 
     ret.m_enemy_drones = this.m_enemy_drones | other.m_enemy_drones; 
     ret.m_enemy_pirates = this.m_enemy_pirates | other.m_enemy_pirates; 

     return ret; 
    } 

    public override bool Equals(object value) 
    { 
     ResourcePack other = value as ResourcePack; 

     return !object.ReferenceEquals(null, other) 
      && int.Equals(m_pirates, other.m_pirates) 
      && int.Equals(m_islands, other.m_islands) 
      && int.Equals(m_enemy_drones, other.m_enemy_drones) 
      && int.Equals(m_enemy_pirates, other.m_enemy_pirates); 
    } 

    /* 
    public override int GetHashCode() 
    { 
     unchecked 
     { 
      int hash = (int)2166136261; 
      hash = (hash * 16777619)^m_pirates.GetHashCode(); 
      hash = (hash * 16777619)^m_islands.GetHashCode(); 
      hash = (hash * 16777619)^m_enemy_drones.GetHashCode(); 
      hash = (hash * 16777619)^m_enemy_pirates.GetHashCode(); 
      return hash; 
     } 
    }*/ 

    public static bool operator ==(ResourcePack a, ResourcePack b) 
    { 
     if (object.ReferenceEquals(a, b)) 
      return true; 

     if (object.ReferenceEquals(null, a)) 
      return false; 

     return a.Equals(b); 
    } 

    public static bool operator !=(ResourcePack a, ResourcePack b) 
    { 
     return !(a == b); 
    } 
} 

.

Dictionary<ResourcePack, T> current = new Dictionary<ResourcePack, T>(missions[0].Length); 
// current[t1] = t2; where t1 is an instance of ResourcePack 

내가 GetHashCode를 오버라이드 (override)하지 말아 때가 가정으로, 코드가 작동합니다 ResourcePack가로 사용되는 있는 유일한 방법은 사전의 핵심 지표이다. 재정의 할 때 (GetHashCode 주석 처리 해제) 코드가 동일하게 작동하지 않고 출력이 무작위로 보입니다.

제발 누군가 나에게 이런 행동을 설명해 주시겠습니까? 내 사용자 정의 GetHashCode가 충분하지 않습니까?

업데이트 나는 당신의 제안을 읽은 후 내 코드를 변경했습니다,하지만 여전히 GetHashCode이 (정도?) 값 고유의 반환하지 않는 것 같다.

새로운 코드 : 나는라는 영역을 제거하면

public sealed class ResourcePack 
{ 
    private readonly int m_pirates; 
    private readonly int m_islands; 
    private readonly int m_enemy_drones; 
    private readonly int m_enemy_pirates; 

    public ResourcePack(int pirates, int islands, int enemy_drones, int enemy_pirates) 
    { 
     this.m_pirates = pirates; 
     this.m_islands = islands; 
     this.m_enemy_drones = enemy_drones; 
     this.m_enemy_pirates = enemy_pirates; 
    } 

    public ResourcePack(ResourcePack other) 
    { 
     this.m_pirates = other.m_pirates; 
     this.m_islands = other.m_islands; 
     this.m_enemy_drones = other.m_enemy_drones; 
     this.m_enemy_pirates = other.m_enemy_pirates; 
    } 

    public bool CanConcatinate(ResourcePack other) 
    { 
     if ((this.m_islands & other.m_islands) != 0) 
     { 
      return false; 
     } 

     if ((this.m_enemy_drones & other.m_enemy_drones) != 0) 
     { 
      return false; 
     } 

     if ((this.m_enemy_pirates & other.m_enemy_pirates) != 0) 
     { 
      return false; 
     } 

     return true; 
    } 

    public ResourcePack SemiConcatinate(ResourcePack other) 
    { 
     return new ResourcePack(this.m_pirates | other.m_pirates, 
      this.m_islands | other.m_islands, 
      this.m_enemy_drones | other.m_enemy_drones, 
      this.m_enemy_pirates | other.m_enemy_pirates); 
    } 

    #region Hashing 

    public override bool Equals(object value) 
    { 
     ResourcePack other = value as ResourcePack; 

     return !object.ReferenceEquals(null, other) 
      && int.Equals(m_pirates, other.m_pirates) 
      && int.Equals(m_islands, other.m_islands) 
      && int.Equals(m_enemy_drones, other.m_enemy_drones) 
      && int.Equals(m_enemy_pirates, other.m_enemy_pirates); 
    } 

    public override int GetHashCode() 
    { 
     unchecked 
     { 
      int hash = (int)23; 
      hash = (hash * 17)^m_pirates.GetHashCode(); 
      hash = (hash * 17)^m_islands.GetHashCode(); 
      hash = (hash * 17)^m_enemy_drones.GetHashCode(); 
      hash = (hash * 17)^m_enemy_pirates.GetHashCode(); 
      return hash; 
     } 
    } 

    public static bool operator ==(ResourcePack a, ResourcePack b) 
    { 
     if (object.ReferenceEquals(a, b)) 
      return true; 

     if (object.ReferenceEquals(null, a)) 
      return false; 

     return a.Equals(b); 
    } 

    public static bool operator !=(ResourcePack a, ResourcePack b) 
    { 
     return !(a == b); 
    } 

    #endregion 
} 

"해싱"모든 것이 잘 작동합니다.

+0

해시에 대한 유사한 알고리즘은 어디에서 찾을 수 있습니까? 필드 해시를 훨씬 단순하게 XOR하는 이유는 무엇입니까? –

+0

@MarioVernari 이미 시도했지만 작동하지 않았고 코드는 여전히 다르게 동작했습니다. 코드를 손상시키지 않는 유일한 방법은 내부 GetHashCode입니다. – user3601822

+4

'GetHashCode'는 읽기 전용 멤버에서만 사용해야합니다. 해시 코드는 사전에서 올바른 버킷을 찾는 데 사용됩니다. 메서드가 다른 결과를 반환하면 사전은 다른 버킷을 선택합니다. – CSharpie

답변

0

당신이 GetHashCode를 오버라이드 (override)하지 않는, 사전은 객체 참조 (동기 블록)에 의해 색인이 생성됩니다. 잃어버린 키의 새로운 인스턴스를 만드는 방법은 없습니다.

한편, GetHashCodeEquals을 모두 구현하면 동일한 필드를 사용하여 키를 "다시 만들 수 있습니다." 그러나 동시에 동일한 ResourcePack 키가 기본적으로 고유하지 않음을 의미합니다. 동일한 필드가있는 경우 이 사전의 동일한 항목에 매핑됩니다. 당신이 볼 수있는 문제에 대한

가장 큰 원인은 다음과 같습니다

  • 당신은 해시 코드 필드를 변이.
  • 두 개의 (이상) ResourcePack 개체가 다르지만 실제로는 동일한 필드가 있습니다. 기본 구현에서는 고유하게 유지하지만 그렇지 않습니다.
+0

@ user3601822 나는 그것이 당신이 시도하려고했던 것이라고 생각했습니다. 분명히 - – CSharpie

4

GetHashCode은 개체가 동일한 사전 내에서 사용되는 동안 동일한 값을 반환해야합니다. 자세한 내용은 documentation을 참조하십시오.

Dictionary<TKey,TValue>은 HashCode를 사용하여 요소를 추가, 제거 및 제거 할 때 전달 된 키의 버킷을 찾습니다.

사전에 쓰기와 읽기 사이의 해시 코드를 변경하면 잘못된 버킷을 선택하여 요소를 찾아 예기치 않은 결과를 얻을 수 있습니다.

성능이 중요하지 않은 경우, 기존의 것들을 사용하지 않으십시오. 튜플의 GetHashCode

Tuple.Create(m_pirates,m_islands,m_enemy_drones,m_enemy_pirates).GetHashCode(); 
+0

먼저 답장을 보내 주셔서 감사합니다. 제안 사항을 사용했지만 여전히 작동하지 않는 것 같습니다. 작동하는 유일한 "사용자 정의"GetHashCode는 다음과 같습니다. return new [] {m_pirates, m_islands, m_enemy_drones, m_enemy_pirates} .GetHashCode() – user3601822

+0

@ user3601822 Jon Skeet이 작성한 내용을 다음 try here http://stackoverflow.com/a/1646913/ 1789202 – CSharpie

+0

그것은 매우 중요한 성능이며, 이것이 내가 맞춤형 GetHashCode를 구현하려는 이유입니다. 내가 말했듯이, 코드는 커스텀 GetHashCode없이 잘 동작하지만, 커스텀 코드로부터 이익을 얻을 수있다. Skeet의 구현을 시도했지만 더 잘 작동하는 것처럼 보였지만 여전히 고유 한 Skeet 구현은 아닙니다. 누구든지 네이티브 구현이 어떻게 작동하는지 알고 있습니까? – user3601822