为什么不';在个人类上使用Dictionaries时,我不需要覆盖GetHashCode

本文关键字:Dictionaries 不需要 GetHashCode 覆盖 人类 为什么不 | 更新日期: 2023-09-27 18:09:09

它似乎总是"工作"而不需要做任何事情。

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

我从未重写过GetHashCode,但我四处阅读,人们说你只需要在重写Equals并为你的应用程序提供自定义相等性检查时才需要重写,所以我想我还好吧?

尽管=p

为什么不';在个人类上使用Dictionaries时,我不需要覆盖GetHashCode

它似乎总是"工作"而不需要做任何事情。

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

如果使用值类型,则EqualsGetHashCode的默认实现是可以的(Equals检查字段是否相等,GetHashCode基于字段(不一定是所有字段!((。如果您使用引用类型,EqualsGetHashCode的默认实现使用引用相等,这可能没问题,也可能没问题;这取决于你在做什么。

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

没有。默认值是基于值类型的字段和引用类型的引用的哈希代码。

(还有,有人知道Object.GetHashCode是如何实现的吗?我在.NET Reflector中找不到它(

这是一个实现细节,你永远不应该知道,也永远不应该依赖它。它随时可能对你产生影响。

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

默认相等对你来说可以吗?如果没有,请覆盖EqualsGetHashCode,或为您的T执行IEqualityComparer<T>

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

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

  1. 对于值类型,Equals是值相等
  2. 对于引用类型,Equals是引用相等
  3. 对于值类型,GetHashCode基于字段(同样,不一定是所有字段!(
  4. 对于引用类型,GetHashCode基于引用

如果您使用Dictionary构造函数的重载,而该重载不将IEqualityComparer<T>作为T,则它将使用EqualityComparer<T>.Default。这个IEqualityComparer<T>只使用了EqualsGetHashCode。因此,如果您还没有覆盖它们,那么您将获得上面定义的实现。如果覆盖EqualsGetHashCode,则这就是EqualityComparer<T>.Default将使用的内容。

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

您是否将自定义类用作键或值?如果您仅将它们用于值,则它们的GetHashCode无关紧要。

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

Object.GetHashCode的文档非常清楚:

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

ObjectEquals()GetHashCode()(您正在继承(的实现通过引用进行比较
CCD_ 37以本机代码实现;您可以在SSCLI(转子(中看到它。

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

如果要按值进行比较,只需要覆盖它们–如果要将具有相同属性的不同实例进行相等比较。

它实际上取决于您对Equality的定义。

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&CCD_ 39以获得适当的行为。

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

哈希表的工作方式是每个对象实现一个函数来计算哈希代码,希望它尽可能分布在int范围内。两个不同的对象可以生成相同的哈希代码,但给定对象数据的对象实例应该始终生成相同的散列代码。因此,它们不是唯一的,不应用于平等。哈希表分配一个大小为n(远小于int范围(的数组,当向哈希表中添加对象时,它会调用GetHashCode,然后根据分配的数组大小对其进行mod'd(%(。对于表中的冲突,通常会链接一个对象列表。由于计算散列码应该非常快,所以查找是快速的——跳到数组偏移量并遍历链。数组越大(内存越多(,冲突就越少,查找也就越快。

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

如果需要比较两个项目,则重写equals。如果您需要对象是可排序的(排序列表需要(,那么覆盖IComparable。

希望这有助于解释差异。

相关文章: