C# 为什么在添加到集合时不对所有对象调用Equals()

C# 为什么在添加到集合时不对所有对象调用Equals(),c#,.net,collections,C#,.net,Collections,我在IDictionary中使用了一个类型作为键。类型如下 public class Employee { public string Name { get; set; } public int ID { get; set; } public override bool Equals(object obj) { Employee emp = obj as Employee; if (emp != null)

我在IDictionary中使用了一个类型作为键。类型如下

public  class Employee
{
    public string Name { get; set; }
    public int ID { get; set; }

    public override bool Equals(object obj)
    {
        Employee emp = obj as Employee;
        if (emp != null)
            return emp.Name.Equals(this.Name);
        return false;
    }

    public override int GetHashCode()
    {
        return this.Name.GetHashCode();
    }
}
现在,我已经创建了一个字典,如下所示

 IDictionary<Employee, int> empCollection = new Dictionary<Employee, int>();
        Employee emp1 = new Employee() { Name = "abhi", ID = 1 };
        Employee emp2 = new Employee() { Name = "vikram", ID = 2 };
        Employee emp3 = new Employee() { Name = "vikram", ID = 3 };

        empCollection.Add(emp1, 1);
        empCollection.Add(emp2, 2);
        empCollection.Add(emp3, 3);
IDictionary empCollection=new Dictionary();
Employee emp1=new Employee(){Name=“abhi”,ID=1};
Employee emp2=new Employee(){Name=“vikram”,ID=2};
Employee emp3=new Employee(){Name=“vikram”,ID=3};
添加(emp1,1);
添加(emp2,2);
empCollection.Add(emp3,3);
现在在调试时,我发现当emp1添加到集合中时,只调用键类型的GetHashCode方法,之后当emp2添加到集合中时,只再次调用GetHashCode方法,但在emp3的情况下,同时调用GetHashCode和Equals方法


也许问这个问题看起来太幼稚了,但为什么在将eqImp2对象添加到集合中时不调用Equals方法呢。里面发生了什么。请解释。

字典和所有其他类似容器使用hashcode作为一种快速而肮脏的检查:不同的hashcode意味着两个对象肯定不相等;相同的哈希代码没有任何意义。的文档通过以下方式指定此行为:

如果两个对象比较相等,则每个对象的GetHashCode方法 对象必须返回相同的值。但是,如果两个对象没有 比较为相等,两个对象的GetHashCode方法不相同 必须返回不同的值


您的
emp1
emp2
生成不同的hashcode,因此字典不需要运行
Equals
;它已经知道它们是不平等的。另一方面,
emp2
emp3
生成相同的hashcode,因此字典必须调用
Equals
,以确定它们是否确实相等,或者相同的hashcode是否只是偶然的结果。

这是因为GetHashCode是一种快捷方式。 C#将首先调用GetHashCode,这应该是快速执行的。 如果两个对象具有不同的hashcode,则无需调用更昂贵的Equals方法。
只有当它们具有相同的哈希代码时,它才会调用Equals。这是因为HashCode不能保证是唯一的

在您的示例中,
GetHashCode
查看名称hash code。emp3与emp2同名(“维克拉姆”)。给定散列码,它们是相等的,因此它使用
Equals
进一步查看

emp2和emp3具有相同的密钥。这将导致字典中出现“键冲突”。它首先调用GetHashCode()并确定哈希代码是相同的。然后,它通过调用Equals()确保它们是相同的。字典中的代码是:

int num = this.comparer.GetHashCode(key) & 2147483647;
...
if (this.entries[i].hashCode == num && this.comparer.Equals(this.entries[i].key, key))
显然,如果哈希代码不匹配,它就永远不必调用Equals


您应该获得像ILSpy这样的工具,然后您可以查看代码并自己找到答案。

如果您继续此实验,您将观察到特定于
字典
实现的一些行为,以及由于您实现
GetHashCode
的方式而需要的一些行为

首先,在比较对象是否相等时,理解
GetHashCode
Equals
的作用很重要。上提供了更多信息,但我将在此重复基本规则:

  • Equals
    方法准确地确定了哪些对象是相等的,哪些对象不是相等的。在返回之前,需要在此方法中进行所有必要的检查,以便最终确定
  • 哈希代码是根据对象的值计算的值。通常,它比原始对象小得多(在我们的例子中,哈希代码是一个4字节的整数),并且不一定是唯一的。但是,与原始对象本身相比,计算和相互比较要快得多。
    • 当哈希代码不需要是唯一的时,不同的哈希代码表示不同的对象(即
      Equals
      肯定会返回false),但相等的哈希代码并不意味着什么(即
      Equals
      可能返回true或false)
  • 将值与键对象关联的集合(例如.NET中的
    IDictionary
    ,或Java中的
    Map
    )利用哈希代码提高实现效率。但是,由于特定的文档不要求结果是唯一的,因此这些集合不能仅依靠哈希代码来实现适当的功能当两个对象具有相同的哈希代码时,只有调用
    Equals
    才能区分它们。
    您描述的插入
    emp3
    的情况属于这种情况:[
    IDictionary.Add
    ]方法需要抛出
    ArgumentException
    ,如果您试图插入相同的值,只有调用
    Equals
    才能确定新键
    emp3
    是否等于先前插入的
    emp3

    其他实施特征

    特定的集合实现可能会导致对
    GetHashCode
    的调用比您预期的要多。例如,当调整集合的内部存储的大小时,实现可能会为集合中存储的每个对象调用
    GetHashCode
    。基于or的集合可能只调用一次
    GetHashCode
    (如果结果缓存在树结构中),或者可能需要在每次插入或查找操作期间为多个对象调用
    GetHashCode
    (如果结果未缓存)

    有时,哈希表实现需要为多个对象调用
    GetHashCode
    ,或者甚至为具有不同哈希代码的对象调用
    Equals
    ,因为它们使用module