为什么在个人课程中使用词典时我不必覆盖GetHashCode?

时间:2011-09-18 16:56:08

标签: c# hash dictionary hashtable

似乎只是“工作”而不必做任何事情。

我唯一能想到的是每个类都有Object.GetHashCode使用的隐藏式静态标识符。 (还有,有谁知道如何实现Object.GetHashCode?我在.NET Reflector中找不到它)

我从来没有覆盖GetHashCode但我正在四处阅读,人们说你只需要覆盖Equals并为你的应用程序提供自定义等式检查,所以我想我没事?

我仍然想知道魔法是如何运作的,尽管= P

5 个答案:

答案 0 :(得分:3)

  

似乎只是“工作”而不必做任何事情。

您没有告诉我们您是否使用了键的值类型或引用类型。

如果您使用的是值类型,EqualsGetHashCode的默认实现就可以了(Equals检查字段是否相等,GetHashCode是否基于这些领域(不一定都是全部!))。如果您正在使用引用类型,EqualsGetHashCode的默认实现使用引用相等,这可能是也可能不是;这取决于你在做什么。

  

我唯一能想到的是每个类都有Object.GetHashCode使用的隐藏式静态标识符。

没有。默认值是基于值类型的字段的哈希码,以及引用类型的引用。

  

(另外,有谁知道如何实现Object.GetHashCode?我在.NET Reflector中找不到它)

这是一个你永远不需要知道的实现细节,永远不会依赖它。随时都可能改变你。

  

我从来没有覆盖过GetHashCode,但是我正在四处阅读,人们说你只需要覆盖Equals并为你的应用程序提供自定义相等检查,所以我想我没事?

嗯,默认的平等对你好吗?如果没有,请覆盖Equals的{​​{1}}和GetHashCode或implmenet IEqualityComparer<T>

  

我仍然想知道魔法是如何运作的,尽管= P

每个对象都有TEquals。默认实现如下:

  1. 对于值类型,GetHashCode是值相等。
  2. 对于参考类型,Equals是引用相等。
  3. 对于值类型,Equals基于字段(同样,不一定都是这些字段!)。
  4. 对于参考类型,GetHashCode基于参考。
  5. 如果您使用GetHashCode构造函数的重载但Dictionary未使用IEqualityComparer<T>,则会使用T。此EqualityComparer<T>.Default仅使用IEqualityComparer<T>Equals。因此,如果您没有覆盖它们,则可以获得上面定义的实现。如果您覆盖GetHashCodeEquals,那么这就是GetHashCode将使用的内容。

    否则,将EqualityComparer<T>.Default的自定义实现传递给IEqualityComparer<T>的构造函数。

答案 1 :(得分:1)

您是使用自定义类作为键还是值?如果您仅将它们用于值,那么它们的GetHashCode无关紧要。

如果您将它们用作键,则哈希的质量会影响性能。 Dictionary存储每个哈希码的元素列表,因为哈希码不需要是唯一的。在最糟糕的情况下,如果所有密钥最终都具有相同的哈希码,那么字典的查找时间就像列表O(n),而不是像哈希表那样O(1)。

Object.GetHashCode的文档非常clear

  

GetHashCode方法的默认实现不保证不同对象的唯一返回值...因此,此方法的默认实现不得用作散列目的的唯一对象标识符。

答案 2 :(得分:0)

Object Equals()GetHashCode()(您继承的)的实施方式通过引用进行比较。
Object.GetHashCode在本机代码中实现;你可以在SSCLI(转子)中看到它。

类的两个不同实例(通常)具有不同的哈希码,即使它们的属性相等。

如果要按值进行比较,则只需要覆盖它们 - 如果您希望具有相同属性的不同实例进行比较,则需要覆盖它们。

答案 3 :(得分:0)

这实际上取决于你对平等的定义。

class Person
{
    public string Name {get; set;}
}

void Test()
{
    var joe1 = new Person {Name="Joe"};
    var joe2 = new Person {Name="Joe"};

    Assert.AreNotEqual(joe1, joe2);
}

如果您对平等有不同的定义,则应覆盖Equals&amp; GetHashCode以获得适当的行为。

答案 4 :(得分:0)

哈希码用于优化哈希表(字典)中的查找性能。虽然哈希码的目标是在对象实例之间尽可能少地发生冲突,但是它们不能保证是唯一的。给定一组典型类型的对象,目标应该是int范围内的平均分配。

散列表的工作方式是每个对象实现一个函数来计算散列码,希望在int范围内尽可能地分布。两个不同的对象可以生成相同的哈希码,但是给定它的数据的对象的实例应该总是产生相同的哈希码。因此,它们不是唯一的,不应该用于平等。哈希表分配一个大小为n的数组(远小于int范围),当一个对象被添加到哈希表时,它调用GetHashCode,然后根据分配的数组大小调整(%)。对于表中的冲突,通常会链接对象列表。由于计算哈希码应该非常快,因此查找很快 - 跳转到数组偏移并走链。数组越大(内存越多),冲突越少,查找越快。

对象GetHashCode不可能生成一个好的哈希代码,因为根据定义,它对从它继承的具体对象一无所知。这就是为什么如果你有自定义对象需要放在字典中并且想要优化查找(控制创建一个具有最小冲突的均匀分布),你应该重写GetHashCode。

如果您需要比较两个项目,则覆盖等于。如果您需要对象可排序(排序列表需要),则覆盖IComparable。

希望这有助于解释差异。