c#如何计算对象的哈希码?

Max*_*kin 12 c# hash internals

这个问题来自关于元组的讨论.

我开始考虑元组应该具有的哈希码.如果我们接受KeyValuePair类作为元组怎么办?它不会覆盖GetHashCode()方法,所以可能它不会知道它的"子"的哈希码...所以,运行时将调用Object.GetHashCode(),它不知道真实的对象结构.

然后我们可以创建一些引用类型的实例,它们实际上是Equal,因为重载的GetHashCode()和Equals().并使用它们作为元组中的"孩子"来"欺骗"字典.

但它不起作用!运行时以某种方式计算出我们元组的结构并调用我们类的重载GetHashCode!

它是如何工作的?Object.GetHashCode()的分析是什么?

当我们使用一些复杂的密钥时,它会在某些不好的情况下影响性能吗?(可能,不可能的情况......但仍然)

以此代码为例:

namespace csharp_tricks
{
    class Program
    {
        class MyClass
        {
            int keyValue;
            int someInfo;

            public MyClass(int key, int info)
            {
                keyValue = key;
                someInfo = info;
            }

            public override bool Equals(object obj)
            {
                MyClass other = obj as MyClass;
                if (other == null) return false;

                return keyValue.Equals(other.keyValue);
            }

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

        static void Main(string[] args)
        {
            Dictionary<object, object> dict = new Dictionary<object, object>();

            dict.Add(new KeyValuePair<MyClass,object>(new MyClass(1, 1), 1), 1);

            //here we get the exception -- an item with the same key was already added
            //but how did it figure out the hash code?
            dict.Add(new KeyValuePair<MyClass,object>(new MyClass(1, 2), 1), 1); 

            return;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新我想我已经在下面的答案中找到了解释.它的主要成果是:

  • 小心你的密钥及其哈希码:-)
  • 对于复杂的字典键,您必须正确覆盖Equals()和GetHashCode().

Pop*_*lin 14

不要在可变类上覆盖GetHashcode()和Equals(),只在不可变类或结构上覆盖它,否则如果修改用作键的对象,哈希表将不再正常工作(你将无法在修改密钥对象后检索与密钥关联的值)

哈希表也不使用哈希码来识别他们使用密钥对象自己作为标识符的对象,并不要求用于在哈希表中添加条目的所有密钥都返回不同的哈希码,但是建议他们这样做,否则性能受苦很大


Max*_*kin 1

看来我现在已经有了线索了。

我以为KeyValuePair是一个引用类型,但其实不是,它是一个结构体。因此它使用 ValueType.GetHashCode() 方法。MSDN 上说:“派生类型的一个或多个字段用于计算返回值”。

如果您将真正的引用类型作为“元组提供者”,您将欺骗字典(或您自己......)。

using System.Collections.Generic;

namespace csharp_tricks
{
    class Program
    {
        class MyClass
        {
            int keyValue;
            int someInfo;

            public MyClass(int key, int info)
            {
                keyValue = key;
                someInfo = info;
            }

            public override bool Equals(object obj)
            {
                MyClass other = obj as MyClass;
                if (other == null) return false;

                return keyValue.Equals(other.keyValue);
            }

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

        class Pair<T, R>
        {
            public T First { get; set; }
            public R Second { get; set; }
        }

        static void Main(string[] args)
        {
            var dict = new Dictionary<Pair<int, MyClass>, object>();

            dict.Add(new Pair<int, MyClass>() { First = 1, Second = new MyClass(1, 2) }, 1);

            //this is a pair of the same values as previous! but... no exception this time...
            dict.Add(new Pair<int, MyClass>() { First = 1, Second = new MyClass(1, 3) }, 1);

            return;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)