GetHashCode中断代码

时间:2017-01-29 09:18:01

标签: c# .net dictionary gethashcode

在我的代码中,我有以下类:

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);
    }
}

只有一个地方使用此代码,一旦创建了ResourcePack实例,成员就不再发生变化。 使用ResourcePack的唯一方法是字典的关键索引:

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

当我不重写GetHashCode时,代码按预期工作。 当我覆盖(取消注释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
}

如果我删除名为&#34; Hashing&#34;一切正常。

2 个答案:

答案 0 :(得分:4)

在对象字典中使用对象时,

GetHashCode必须返回相同的值。有关详细信息,请参阅documentation

Dictionary<TKey,TValue>使用HashCode来查找添加,获取和删除元素时传递的Key的存储桶。

将写入和阅读之间的哈希代码更改为字典会使其选择错误的存储桶来查找元素,从而为您带来意想不到的结果。

如果它不是超级性能关键,为什么不利用一些现有的东西,例如元组的GetHashCode

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

答案 1 :(得分:0)

当您不覆盖GetHashCode时,字典会被对象引用(同步块)编入索引。如果密钥丢失,则无法创建密钥的新实例。

另一方面,您GetHashCodeEquals的实施可以重新创建&#34;关键只需使用相同的字段。但与此同时,这意味着您的ResourcePack密钥默认情况下不再是唯一的 - 如果它们具有相同的字段,则映射到字典中的相同项目。< / p>

您遇到问题的最可能原因是:

  • 你改变了哈希码字段。
  • 您有两个(或更多)ResourcePack个对象应该是不同的,但实际上具有相同的字段。默认实现使它们保持唯一,你的没有。