使用不同的属性覆盖GetHashCode

时间:2016-01-28 14:02:01

标签: c# override

我有这个对象:

public class Foo  {
    public string MyOwnId { get; set; }
    public Guid FooGuid { get; } = Guid.NewGuid();
}

我希望Equals()只关心那些MyOwnId的人,否则他们永远不会相等。如果FooMyOwnId,我会尝试使用它,否则我想使用FooGuid

由于FooGuid可能永远不会相同,我做了类似的事情:

public bool Equals(Foo foo) {
        if (foo== null) return false;
        return MyOwnId.Equals(foo.MyOwnId);
    }

    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((Foo)obj);
    }

    public override int GetHashCode() {
        int hash = 13;
        hash = (hash*7) + (!string.IsNullOrEmpty(MyOwnId) ? MyOwnId.GetHashCode() : FooGuid.GetHashCode());
        return hash;
    }

这是做我想要的正确方法吗?或者我还需要更改我的Equals方法,以便它看起来像GetHashCode一样?例如:

public bool Equals(Foo foo) {
        if (foo == null) return false;
        if (string.IsNullOrEmpty(MyOwnId) || string.IsNullOrEmpty(foo.MyOwnId)) return false;
        return MyOwnId.Equals(foo.MyOwnId);
    }

3 个答案:

答案 0 :(得分:2)

好吧,让我们看看。您对EqualsGetHashCode的实施错误

EqualsGetHashCode都不得抛出异常;反例是

  Foo A = new Foo();
  Foo B = new Foo() {
    MyOwnId = "bla-bla-bla",
  };

  // Throws an exception
  if (A.Equals(B)) {}

如果两个实例通过Equals相等,则这些实例必须具有相同的哈希码;反例是

  Foo A = new Foo() {
    MyOwnId = "",
  };

  Foo B = new Foo() {
    MyOwnId = "",
  };

  if (A.Equals(B)) {
    // Hashcodes must be equal and they are not
    Console.Write(String.Format("{0} != {1}", A.GetHashCode(), B.GetHashCode()));
  }

可能(最简单)的实施

// since you've declared Equals(Foo other) let others know via interface implementation
public class Foo: IEquatable<Foo> { 
  public string MyOwnId { get; set; }
  public Guid FooGuid { get; } = Guid.NewGuid();

  public bool Equals(Foo other) {
    if (Object.ReferenceEquals(this, other))
      return true;
    else if (Object.ReferenceEquals(null, other))
      return false;
    else
      return String.Equals(MyOwnId, other.MyOwnId);
  }

  public override bool Equals(object obj) {
    return Equals(obj as Foo); // do not repeat youself: you've got Equals already
  }

  public override int GetHashCode() {
    // String.GetHashCode is good enough, do not re-invent a wheel
    return null == MyOwnId ? 0 : MyOwnId.GetHashCode(); 
  }
}

答案 1 :(得分:1)

  

或者我是否还需要更改我的Equals方法,使它看起来像我的GetHashCode一样?

您更改Equals以匹配您希望解决相等的方式。你做到了。

您将GetHashCode()更改为键入相同的信息。在这种情况下:

public override int GetHashCode()
{
  return MyOwnId == null ? 0 : MyOwnId.GetHashCode();
}

顺便提一下,您的Equals(object)有点过于复杂。我会用:

public override bool Equals(object obj)
{
  return Equals(obj as Foo);
}

这将obj为空的情况传递给特定的Equals()(必须处理它),处理obj不是{{1}通过传递Foo一个null(无论如何都是假的)并将Equals()的案例处理从obj传递给更具体的事物(这也是必须的处理那个)。

Foo的快捷方式在这里不值得做,因为只有一个字段被比较,并且它的比较将具有相同的ReferenceEquals快捷方式。您不会将ReferenceEquals作为专用foo中的派生类型处理。如果Foo未被封存,则应包括:

Foo

如果密封public bool Equals(Foo foo) { return (object)foo != null && foo.GetType() == GetType() && MyOwnId.Equals(foo.MyOwnId); } ,则应省略Foo比较。

如果GetType()的逻辑比这更复杂,那么就像:

Equals()

确实是有益的,但同样应该是特定的过载,而不是一般的覆盖。

(在public bool Equals(Foo foo) { if ((object)foo == (object)this) return true; return (object)foo != null && foo.GetType() == GetType() && // Some more complicated logic here. } 重载中执行引用相等性检查更有利,因为它们必须考虑两个操作数都为null的可能性,因此它们也可能认为它们都是相同的,它们隐含地包含那个案子)。

答案 2 :(得分:0)

哈希函数必须具有以下属性:

  • 如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较不相等,则两个对象的GetHashCode方法不必返回不同的值。
  • 对象的GetHashCode方法必须始终返回相同的哈希码,只要不对对象状态进行修改即可确定对象的Equals方法的返回值。请注意,这仅适用于当前应用程序的执行,并且如果再次运行应用程序,则可以返回不同的哈希代码。
  • 为获得最佳性能,哈希函数应为所有输入生成均匀分布,包括严重群集的输入。这意味着对对象状态的小修改应该导致对结果哈希代码的大量修改,以获得最佳哈希表性能。
  • 哈希函数的计算成本应该低廉。
  • GetHashCode方法不应抛出异常。

请参阅https://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx