繁体   English   中英

在个人类上使用字典时,为什么不必覆盖GetHashCode?

[英]Why don't I ever have to override GetHashCode when using Dictionaries on personal classes?

它似乎总是“工作”而无需做任何事情。

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

我从未覆盖过GetHashCode但我一直在阅读,人们说,您只需要在覆盖Equals并为您的应用程序提供自定义相等性检查时才这样做,所以我想我很好吗?

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

它似乎总是“工作”而无需做任何事情。

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

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

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

否。默认值为基于值类型的字段和引用类型的引用的哈希码。

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

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

我从未覆盖过GetHashCode,但我一直在阅读,人们说,您只需要在覆盖Equals并为您的应用程序提供自定义相等性检查时才这样做,所以我想我很好吗?

好吧,默认相等对您还好吗? 如果没有,覆盖EqualsGetHashCode或implmenet IEqualityComparer<T>为你T

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

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

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

如果您使用的Dictionary构造函数的重载没有为您的T携带IEqualityComparer<T> ,则它将使用EqualityComparer<T>.Default IEqualityComparer<T>仅使用EqualsGetHashCode 因此,如果您没有覆盖它们,则可以得到上面定义的实现。 如果覆盖EqualsGetHashCode则将使用EqualityComparer<T>.Default

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

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

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

Object.GetHashCode的文档非常清楚

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

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

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

仅当要按值比较时才需要覆盖它们–如果要对具有相同属性的不同实例进行相等比较。

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

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);
}

如果对相等性有不同的定义,则应重写EqualsGetHashCode以获得适当的行为。

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

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

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

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

希望能帮助解释差异。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM