GetHashCode重写包含泛型数组的对象

Svi*_*ish 54 c# arrays generics hashcode

我有一个包含以下两个属性的类:

public int Id      { get; private set; }
public T[] Values  { get; private set; }
Run Code Online (Sandbox Code Playgroud)

我做了它IEquatable<T>并覆盖了object.Equals这样的:

public override bool Equals(object obj)
{
    return Equals(obj as SimpleTableRow<T>);
}

public bool Equals(SimpleTableRow<T> other)
{
    // Check for null
    if(ReferenceEquals(other, null))
        return false;

    // Check for same reference
    if(ReferenceEquals(this, other))
        return true;

    // Check for same Id and same Values
    return Id == other.Id && Values.SequenceEqual(other.Values);
}
Run Code Online (Sandbox Code Playgroud)

object.Equals我有覆盖时,我GetHashCode当然也必须覆盖.但是我应该实现什么代码?如何从通用数组中创建哈希码?我如何将它与Id整数相结合?

public override int GetHashCode()
{
    return // What?
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 83

由于这个帖子中出现的问题,我发布了另一个回复,显示如果你弄错了会发生什么......主要是你不能使用数组GetHashCode(); 正确的行为是,当你运行它时没有打印警告...切换注释以修复它:

using System;
using System.Collections.Generic;
using System.Linq;
static class Program
{
    static void Main()
    {
        // first and second are logically equivalent
        SimpleTableRow<int> first = new SimpleTableRow<int>(1, 2, 3, 4, 5, 6),
            second = new SimpleTableRow<int>(1, 2, 3, 4, 5, 6);

        if (first.Equals(second) && first.GetHashCode() != second.GetHashCode())
        { // proven Equals, but GetHashCode() disagrees
            Console.WriteLine("We have a problem");
        }
        HashSet<SimpleTableRow<int>> set = new HashSet<SimpleTableRow<int>>();
        set.Add(first);
        set.Add(second);
        // which confuses anything that uses hash algorithms
        if (set.Count != 1) Console.WriteLine("Yup, very bad indeed");
    }
}
class SimpleTableRow<T> : IEquatable<SimpleTableRow<T>>
{

    public SimpleTableRow(int id, params T[] values) {
        this.Id = id;
        this.Values = values;
    }
    public int Id { get; private set; }
    public T[] Values { get; private set; }

    public override int GetHashCode() // wrong
    {
        return Id.GetHashCode() ^ Values.GetHashCode();
    }
    /*
    public override int GetHashCode() // right
    {
        int hash = Id;
        if (Values != null)
        {
            hash = (hash * 17) + Values.Length;
            foreach (T t in Values)
            {
                hash *= 17;
                if (t != null) hash = hash + t.GetHashCode();
            }
        }
        return hash;
    }
    */
    public override bool Equals(object obj)
    {
        return Equals(obj as SimpleTableRow<T>);
    }
    public bool Equals(SimpleTableRow<T> other)
    {
        // Check for null
        if (ReferenceEquals(other, null))
            return false;

        // Check for same reference
        if (ReferenceEquals(this, other))
            return true;

        // Check for same Id and same Values
        return Id == other.Id && Values.SequenceEqual(other.Values);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Vinko:你能澄清一下吗?你的意思是"为什么哈希码很重要?" - 或"为什么这样做?".鉴于你的代表和答案数,我假设后者; 这只是一种获取散列的方法,该散列将所有值都考虑为"乘以素数并添加下一个散列"是一种非常常见的散列方法,可以避免冲突(对比xor;在这种情况下是"all"的集合8s"很容易给出可预测的哈希码0".我错过了什么吗? (5认同)

Dus*_*ell 31

FWIW,在哈希码中使用Values的内容非常危险.如果您能保证永远不会改变,那么您应该这样做.但是,由于它暴露,我不认为保证它是可能的.对象的哈希码永远不会改变.否则,它将作为Hashtable或Dictionary中的键丢失其值.考虑使用对象作为Hashtable中的键的难以发现的错误,其哈希码因外部影响而发生变化,您无法再在Hashtable中找到它!

  • 这需要更多的投票.我总是在GetHashCode的概念和下载文件的"MD5哈希"之间做出错误的假设.GetHashCode并不是要比较内容,而是容器.确保它指向内存中的相同位置.我使用GetHashCode来验证自上次将对象保存到数据库后是否发生了更改.我保留了一个克隆列表来比较对象,但是在重写GetHashCode之后,基于哈希表的所有内容都开始表现得很奇怪.现在我只是将我的覆盖移动到它自己的方法并使用"内容哈希"保留字典 (2认同)