关于c#:为什么不在Equality中使用GetHashCode?

why not just using GetHashCode in Equality?

本问题已经有最佳答案,请猛点这里访问。

根据个人等级:

1
2
3
4
5
class person
{
    public string name;
    public int age;
}

例如,我重写类人员的getHashCode方法:

1
2
3
4
5
6
7
8
9
 public override int GetHashCode()
 {
     unchecked
     {
          hashCode = 17;
          // ...some code here...
     }
     return hashCode;
 }

根据msdn的指示,我还需要重写这个等式,所以我这样做了:

1
2
3
4
5
public override bool Equals(object obj)
{
    // ...something like:
    return this.name == (person)obj.name && this.age ==(person)obj.age;
}

嘿,等等,sine我可以得到Person实例的哈希代码,为什么不在equals中使用哈希代码呢?像:

1
2
3
4
public override bool Equals(object obj)
{
    return this.GetHashCode() == (person)obj.GetHashCode();
}

我在google上发现大多数equals()示例与我以前版本的equals()类似,那么,我是否误解了一些东西?

任何帮助都将不胜感激,谢谢。


两个不相等的对象不能保证具有不相等的哈希代码(这称为冲突)。这就是msdn所说的:

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


这是因为有比哈希码更多的可能性。

例如,让我们来上课。

你已经有问题了,因为年龄范围和int相同。当然,这是可以消除的:只需使用byte就可以了。不过,我们还有一个问题:字符串。.NET字符串是Unicode(UTF-16),因此每个字母可能有65536个字符。之后,它会迅速升级…一个双字符字符串最多可以有65536^2个字符,即4294967296(uint.MaxValue个)的可能性。太多了,只有两个角色。

td;lr:您不能保证两个不相等的对象不具有相同的哈希代码。完全。(除非是byteshortsbyteushort,但这是技术问题)


GetHashCode背后的想法是,如果知道两个对象具有不同的哈希代码,就可以安全地假设它们不相等,而无需查看它们。只有当两个对象的散列码匹配时,才需要进一步检查它们。如果有一个对象集合,其散列代码都不可能与给定对象匹配(例如,因为集合中的所有对象都具有以4591结尾的散列代码,而给定对象的散列代码在2011年结束),则无需检查集合中的任何对象,即可知道它们都不可能与给定对象匹配。

正确编写的代码发现散列代码与给定对象的散列代码匹配的对象时,应该发现这些对象可能匹配,但可能不匹配,并且应该详细扫描这些对象,以查明它们是否确实匹配。如果哈希代码匹配但对象不匹配,则哈希代码匹配的唯一结果应该是增加发现对象不同所需的时间。如果百万分之一的比较生成错误匹配,则预检查哈希代码可以将详细比较的数量减少一百万倍。相比之下,如果散列函数没有那么好,并且千分之一的比较产生错误匹配,则预先检查散列代码将"仅"减少千分之一的详细比较数。当然,尽管千倍的加速不如百万倍的加速,但它可能仍然比不加速要好得多。


如果你想要一个好的例子,试着从resharper的角度来看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Person : IEquatable<Person>
{
    public string Name { get; set; }
    public int Age { get; set; }

    public bool Equals(Person other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return string.Equals(Name, other.Name) && Age == other.Age;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Person) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((Name != null ? Name.GetHashCode() : 0) * 397) ^ Age;
        }
    }
}