20

这篇文章中,Jon Skeet 提到他通常使用这种算法来覆盖GetHashCode()

public override int GetHashCode()
{
  unchecked // Overflow is fine, just wrap
  {
    int hash = 17;
    // Suitable nullity checks etc, of course :)
    hash = hash * 23 + Id.GetHashCode();
    return hash;
  }
}

现在,我已经尝试过使用它,但是 Resharper 告诉我方法GetHashCode()应该只使用只读字段进行散列(尽管它编译得很好)。什么是一个好的做法,因为现在我真的不能让我的字段是只读的?

我尝试通过 Resharper 生成此方法,结果如下。

public override int GetHashCode()
{
  return base.GetHashCode();
}

老实说,这并没有多大贡献...

4

3 回答 3

18

如果您的所有字段都是可变的并且您必须实现GetHashCode方法,那么恐怕这是您需要的实现。

public override int GetHashCode() 
{ 
    return 1; 
} 

是的,这是低效的,但这至少是正确的。

问题是GetHashCodeDictionary 和 HashSet 集合正在使用它来将每个项目放入存储桶中。如果hashcode是根据一些可变字段计算出来的,而对象放入HashSet或Dictionary后,字段确实发生了变化,则无法再从HashSet或Dictionary中找到该对象。

请注意,所有对象都返回相同的 HashCode 1,这基本上意味着所有对象都被放入 HashSet 或 Dictionary 的同一个桶中。因此,HashSet 或 Dictionary 中始终只有一个桶。在尝试查找对象时,它将对唯一存储桶内的每个对象进行相等性检查。这就像在链表中进行搜索。

有人可能会争辩说,如果我们可以确保在将对象添加到 HashCode 或 Dictionary 集合之后字段永远不会更改,那么基于可变字段实现哈希码可能会很好。我个人的看法是,这很容易出错。两年后接管您的代码的人可能没有意识到这一点并意外破坏了代码。

于 2012-07-14T06:18:31.897 回答
5

请注意,您的 GetHashCode 必须与您的 Equals 方法齐头并进。而且,如果您可以只使用引用相等(当您的类的两个不同实例可以相等时),那么您可以安全地使用从 Object 继承的 Equals 和 GetHashCode。这比简单地return 1从 GetHashCode 工作得更好。

于 2012-07-14T15:39:22.897 回答
-2

GetHashCode()我个人倾向于为没有不可变字段的类中的每个实现返回不同的数值。这意味着如果我有一个包含不同实现类型的字典,就有可能将不同类型的不同实例放入不同的存储桶中。

例如

public class A
{
    // TODO Equals override

    public override int GetHashCode()
    {
        return 21313;
    } 
}

public class B
{
    // TODO Equals override

    public override int GetHashCode()
    {
        return 35507;
    } 
}

然后,如果我有,和其他类型的Dictionary<object, TValue>包含实例,则查找的性能将比所有实现返回相同数值的情况更好。ABGetHashCode

还应该注意的是,我使用素数来获得更好的分布。

根据评论,我在这里提供了一个 LINQPad 示例,该示例演示了使用return 1不同类型和为每种类型返回不同值之间的性能差异。

于 2013-10-29T09:54:53.737 回答