我应该如何实现Object.GetHashCode()以实现复杂的相等?

Mat*_*ley 7 c# equals iequatable gethashcode

基本上,到目前为止我有以下内容:

class Foo {
    public override bool Equals(object obj)
    {
        Foo d = obj as Foo ;
        if (d == null)
            return false;

        return this.Equals(d);
    }

    #region IEquatable<Foo> Members

    public bool Equals(Foo other)
    {
        if (this.Guid != String.Empty && this.Guid == other.Guid)
            return true;
        else if (this.Guid != String.Empty || other.Guid != String.Empty)
            return false;

        if (this.Title == other.Title &&
            this.PublishDate == other.PublishDate &&
            this.Description == other.Description)
            return true;

        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,问题是:我有一个非必需字段Guid,这是一个唯一标识符.如果没有设置,那么我需要尝试根据不太准确的度量确定相等性,以尝试确定两个对象是否相等.这很好用,但它会弄得GetHashCode()一团糟......我应该怎么做呢?一个天真的实现将是这样的:

public override int GetHashCode() {
    if (this.Guid != String.Empty)
        return this.Guid.GetHashCode();

    int hash = 37;
    hash = hash * 23 + this.Title.GetHashCode();
    hash = hash * 23 + this.PublishDate.GetHashCode();
    hash = hash * 23 + this.Description.GetHashCode();
    return hash;
}
Run Code Online (Sandbox Code Playgroud)

但是这两种哈希冲突的可能性有多大?当然,我不指望它1 in 2 ** 32.这是一个坏主意,如果是这样,我该怎么做呢?

Joh*_*ica 7

自定义类的一种非常简单的哈希代码方法是将每个字段的哈希代码按位异或.它可以这么简单:

int hash = 0;
hash ^= this.Title.GetHashCode();
hash ^= this.PublishDate.GetHashCode();
hash ^= this.Description.GetHashCode();
return hash;
Run Code Online (Sandbox Code Playgroud)

上面链接:

XOR具有以下不错的属性:

  • 它不依赖于计算顺序.
  • 它不会"浪费"比特.如果您更改其中一个组件中的一位,则最终值将更改.
  • 它是快速的,甚至是最原始的计算机上的单个循环.
  • 它保持均匀分布.如果你组合的两个部分是均匀分布的,那么组合就是这样.换句话说,它不会将摘要的范围折叠成更窄的范围.

如果您希望在字段中具有重复值,则XOR不能正常工作,因为重复值将在XORed时相互抵消.由于您将三个不相关的字段散列在一起,在这种情况下不应该成为问题.

  • 不依赖于计算顺序的XOR是一把双刃剑...如果你有多个相同类型的字段的对象(例如,两个日期),那么当这些对象交换时,对象将"看起来相同"哈希. (7认同)

jer*_*jvl 5

我不认为您选择使用的方法存在问题.担心"太多"哈希冲突几乎总是表明过度思考问题; 只要哈希很可能不同,你应该没问题.

最终Description,如果可以合理地预期大多数时间对象可以根据其标题和出版日期(书籍?)进行区分,那么你甚至可能想要考虑从你的哈希中省略.

您甚至可以考虑完全忽略哈希函数中的GUID,并且仅在Equals实现中使用它来消除哈希冲突的不太可能(?)的情况.