2010-02-07 42 views
2

我是C#的新手。也許我沒有正確實施IEquatable,因爲我認爲應該被認爲是相同的對象不是。C#XNA:字典故障

類:

class CompPoint : IComparable { 
     public int X; 
     public int Y; 

     public CompPoint(int X, int Y) { 
      this.X = X; 
      this.Y = Y; 
     } 

    public override bool Equals(Object o) { 
     if (!(o is CompPoint)) { 
      throw new ArgumentException(String.Format("argument is not a CompPoint (%s given)", o)); 
     } 
     CompPoint cp = (CompPoint)o; 

     return this.X == cp.X && this.Y == cp.Y; 
    } 

    public override int GetHashCode() { 
     int hash = base.GetHashCode(); // this is a problem. replace with a constant? 
     hash = (hash * 73) + this.X.GetHashCode(); 
     hash = (hash * 73) + this.Y.GetHashCode(); 
     return hash; 
    } 
} 

(還有更多的CompPoint超過這一點,並證明它是一個類)

然後,測試失敗:

[TestMethod()] 
    public void compPointTest() { 
     Assert.AreEqual(new CompPoint(0, 0), new CompPoint(0, 0)); 
    } 

什麼時我誤解了? Assert.AreEqual()使用引用平等嗎?我的Equals()功能在CompPoint搞砸了嗎?

此功能也將失敗:

public void EqualsTest() { 
     Assert.IsTrue(new CompPoint(1, 1).Equals(new CompPoint(1, 1))); 
    } 

這種情況的原因是,我使用的是Dictionary,並且它不工作的方式,我倒是希望它會:

[TestMethod()] 
    public void dictCompPointTest() { 
     IDictionary<CompPoint, int> dict = new Dictionary<CompPoint, int>(); 
     dict[new CompPoint(0, 0)] = 4; 
     dict[new CompPoint(0, 0)] = 24; 
     dict[new CompPoint(0, 0)] = 31; 

     Assert.AreEqual(31, dict[new CompPoint(0, 0)]); 
     Assert.AreEqual(1, dict.Count); 
    } 

的測試失敗並顯示以下消息:

測試方法 ShipAILabTest.BoardUtilsTest.dictCompPointTest 引發異常: System.Collections.Generic.KeyNotFoundException: 給定的鍵不存在於 字典中。

這個測試包含了我的期望。我希望由於每次密鑰都是相同的,該值將被覆蓋。什麼是Dictionary用於測試平等?

更新:我添加了一個平等的功能,按托馬斯的建議,現在CompPoint比較試驗工作,並dictCompPointTest作品。

public override bool Equals(Object o) { 
     if (!(o is CompPoint)) { 
      throw new ArgumentException(String.Format("argument is not a CompPoint (%s given)", o)); 
     } 
     CompPoint cp = (CompPoint)o; 

     return this.X == cp.X && this.Y == cp.Y; 
    } 

令人不解,這個測試仍然失敗:在密鑰new CompPoint(4, 1)

[TestMethod()] 
    public void dictCPTest2() { 
     IDictionary<CompPoint, int> dict = new Dictionary<CompPoint, int>(); 
     dict[new CompPoint(2, 2)] = 2; 
     dict[new CompPoint(2, 2)] = 2; 

     Assert.AreEqual(1, dict.Count); 
    } 

測試也失敗了,但不是在密鑰new CompPoint(0, 1)。爲什麼這可能是爲了某些價值觀而不是其他的?

更神祕的是:哈希碼功能似乎工作得很差。此測試失敗:

[TestMethod()] 
    public void hashCodeTest() { 
       int x = 0; 
       int y = 0; 
       Assert.AreEqual(new CompPoint(x, y).GetHashCode(), new CompPoint(x, y).GetHashCode());  
    } 

上面列出了哈希碼功能。這裏有什麼問題?兩個CompPoint對象不應該有相同的散列碼嗎?也許我打電話給base.getHashCode()是個問題?

回答

1

如果你重載Equals那麼你也應該重寫GetHashCode,因爲這是字典將在首先確定兩個鍵是否匹配。 (任何兩個相同類型的被認爲相等的對象應從GetHashCode返回相同的值。)

public class CompPoint : IEquatable<CompPoint> 
{ 
    // ... 

    public override bool Equals(object obj) // object 
    { 
     return this.Equals(obj as ComPoint); 
    } 

    public bool Equals(CompPoint other) // IEquatable<ComPoint> 
    { 
     return !object.ReferenceEquals(other, null) 
      && this.X.Equals(other.X) 
      && this.Y.Equals(other.Y); 
    } 

    public override int GetHashCode() // object 
    { 
     int hash = 5419; 
     hash = (hash * 73) + this.X.GetHashCode(); 
     hash = (hash * 73) + this.Y.GetHashCode(); 
     return hash; 
    } 
} 
+0

這很吸引人,但最終沒有成效。 'Dictionary'仍然無法檢測到重複項。 – 2010-02-08 00:36:20

+0

我覺得'getHashCode()'可能會導致問題。 (見上) – 2010-02-08 00:43:18

+1

不知道你是否修復了這個問題,但是如果你的GetHashCode方法仍然是你在上面的代碼中發佈的,它仍然會爲兩個實例生成一個不同的對象(即兩個新的CompPoint(0, 0))由於他們調用base.GetHashCode() – jeffora 2010-02-08 00:55:18

3

我認爲Assert.AreEqual只是使用Object.Equals而不是IEquatable<T>.Equals。因此,您需要覆蓋Equals以反映IEquatable<T>.Equals的邏輯。

或者你也可以使用Assert.IsTrue

IEquatable<CompPoint> p1 = new CompPoint(0, 0); 
IEquatable<CompPoint> p2 = new CompPoint(0, 0); 
Assert.IsTrue(p1.Equals(p2)); 

注意,我宣佈P1和P2爲IEquatable<CompPoint>:這一點,以確保IEquatable<CompPoint>.Equals被稱爲而非Object.Equals,由於接口被明確實施

編輯:順便說一句,你可能要聲明CompPoint作爲一個結構,而不是一個類。這樣,你甚至不需要實現任何東西,因爲值類型根據字段值進行比較。

+0

+1。打敗我一拳。然而,不要讓'Equals()'複製邏輯,只要讓它調用另一個方法即可。 – 2010-02-07 23:36:49

+0

稍微更新了我的問題。使用'IEquatable '作爲類型而不僅僅是'CompPoint'是否很重要? – 2010-02-07 23:46:03

+0

這很重要,因爲你明確實施了'IEquatable **'**。但是,我不明白爲什麼當你使用你的類型作爲字典鍵時它不起作用:S – 2010-02-07 23:58:56