什么是重写的System.Object.GetHashCode的最佳算法?
  在.NET System.Object.GetHashCode方法中,在.NET基类库中的很多地方都使用了这个方法。  特别是在快速查找集合中的项目或确定平等时。  是否有一个关于如何为我的自定义类实现GetHashCode覆盖的标准算法/最佳实践,所以我不会降低性能? 
我通常使用Josh Bloch的神话般的Effective Java中的实现。 它速度很快,创建了一个相当不错的散列,这不太可能导致冲突。 选择两个不同的素数,例如17和23,然后执行:
public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 17;
        // Suitable nullity checks etc, of course :)
        hash = hash * 23 + field1.GetHashCode();
        hash = hash * 23 + field2.GetHashCode();
        hash = hash * 23 + field3.GetHashCode();
        return hash;
    }
}
正如评论中指出的那样,您可能会发现最好选择一个较大的素数来代替。 显然486187739是好的...尽管大多数我看到的小数字的例子倾向于使用素数,但至少有类似的算法使用非素数。 例如,在后来的不太完整的FNV例子中,我使用的数字显然效果不错 - 但初始值不是主要数据。 (虽然乘法常数是主要的,但我不知道这有多重要。)
  由于两个主要原因,这比通常的XOR更好。  假设我们有一个包含两个int字段的类型: 
XorHash(x, x) == XorHash(y, y) == 0 for all x, y
XorHash(x, y) == XorHash(y, x) for all x, y
顺便说一下,早期的算法是C#编译器当前用于匿名类型的算法。
  本页面提供了很多选项。  我认为在大多数情况下,上述内容“足够好”,而且记住和正确的记录非常容易。  FNV的替代方法同样简单,但使用不同的常量和XOR而不是ADD作为组合操作。  它看起来像下面的代码,但正常的FNV算法对单个字节进行操作,所以这需要修改每个字节执行一次迭代,而不是每个32位散列值。  FNV也被设计用于可变长度的数据,而我们在这里使用的方式始终是相同数量的字段值。  对这个答案的评论表明,这里的代码实际上并没有像上面的添加方法那样工作(在样本案例中被测试过)。 
// Note: Not quite FNV!
public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = (int) 2166136261;
        // Suitable nullity checks etc, of course :)
        hash = (hash * 16777619) ^ field1.GetHashCode();
        hash = (hash * 16777619) ^ field2.GetHashCode();
        hash = (hash * 16777619) ^ field3.GetHashCode();
        return hash;
    }
}
请注意,有一点需要注意的是,理想情况下,您应该在将其添加到依赖哈希代码的集合后,防止对等同敏感(并因此对哈希码敏感)状态发生更改。
根据文件:
您可以为不可变的引用类型重写GetHashCode。 通常,对于可变引用类型,只有在以下情况下才应该重写GetHashCode:
微软已经提供了一个很好的通用HashCode生成器:只需将您的属性/字段值复制到匿名类型并对其进行哈希处理即可:
new { PropA, PropB, PropC, PropD }.GetHashCode();
这将适用于任何数量的属性。 它不使用拳击或额外的资源。 它只是使用匿名类型框架中已经实现的算法。
  这是我的hashcode助手。 
  它的优点是它使用泛型类型参数,因此不会导致装箱: 
public static class HashHelper
{
    public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
    {
         unchecked
         {
             return 31 * arg1.GetHashCode() + arg2.GetHashCode();
         }
    }
    public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
    {
        unchecked
        {
            int hash = arg1.GetHashCode();
            hash = 31 * hash + arg2.GetHashCode();
            return 31 * hash + arg3.GetHashCode();
        }
    }
    public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, 
        T4 arg4)
    {
        unchecked
        {
            int hash = arg1.GetHashCode();
            hash = 31 * hash + arg2.GetHashCode();
            hash = 31 * hash + arg3.GetHashCode();
            return 31 * hash + arg4.GetHashCode();
        }
    }
    public static int GetHashCode<T>(T[] list)
    {
        unchecked
        {
            int hash = 0;
            foreach (var item in list)
            {
                hash = 31 * hash + item.GetHashCode();
            }
            return hash;
        }
    }
    public static int GetHashCode<T>(IEnumerable<T> list)
    {
        unchecked
        {
            int hash = 0;
            foreach (var item in list)
            {
                hash = 31 * hash + item.GetHashCode();
            }
            return hash;
        }
    }
    /// <summary>
    /// Gets a hashcode for a collection for that the order of items 
    /// does not matter.
    /// So {1, 2, 3} and {3, 2, 1} will get same hash code.
    /// </summary>
    public static int GetHashCodeForOrderNoMatterCollection<T>(
        IEnumerable<T> list)
    {
        unchecked
        {
            int hash = 0;
            int count = 0;
            foreach (var item in list)
            {
                hash += item.GetHashCode();
                count++;
            }
            return 31 * hash + count.GetHashCode();
        }
    }
    /// <summary>
    /// Alternative way to get a hashcode is to use a fluent 
    /// interface like this:<br />
    /// return 0.CombineHashCode(field1).CombineHashCode(field2).
    ///     CombineHashCode(field3);
    /// </summary>
    public static int CombineHashCode<T>(this int hashCode, T arg)
    {
        unchecked
        {
            return 31 * hashCode + arg.GetHashCode();   
        }
    }
它也有提供流畅接口的扩展方法,所以你可以像这样使用它:
public override int GetHashCode()
{
    return HashHelper.GetHashCode(Manufacturer, PartN, Quantity);
}
或者像这样:
public override int GetHashCode()
{
    return 0.CombineHashCode(Manufacturer)
        .CombineHashCode(PartN)
        .CombineHashCode(Quantity);
}
上一篇: What is the best algorithm for an overridden System.Object.GetHashCode?
