簡體   English   中英

使用不同的屬性覆蓋GetHashCode

[英]Overriding GetHashCode with different properties

我有這個對象:

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

我希望Equals()只關心那些有MyOwnId ,否則他們永遠不會平等。 Foo有一個MyOwnId我嘗試使用它,否則我想使用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);
    }

好吧,走着瞧。 您對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(); 
  }
}

或者我還需要更改我的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為null的情況傳遞給特定的Equals() (它也必須處理它),通過將Equals()傳遞給null(無論如何都是false)來處理obj是不是Foo的東西並傳遞處理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的可能性,因此它們也可能認為它們都是相同的,隱含地包含該情況)。

哈希函數必須具有以下屬性:

  • 如果兩個對象比較相等,則每個對象的GetHashCode方法必須返回相同的值。 但是,如果兩個對象的比較不相等,則兩個對象的GetHashCode方法不必返回不同的值。
  • 只要沒有對對象狀態的修改來確定對象的Equals方法的返回值,對象的GetHashCode方法必須始終返回相同的哈希代碼。 請注意,這僅適用於當前應用程序的執行,並且如果再次運行應用程序,則可以返回不同的哈希代碼。
  • 為獲得最佳性能,哈希函數應為所有輸入生成均勻分布,包括嚴重群集的輸入。 這意味着對對象狀態的小修改應該導致對結果哈希代碼的大修改以獲得最佳哈希表性能。
  • 散列函數的計算成本應該低廉。
  • GetHashCode方法不應拋出異常。

請參閱https://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM