2010-02-07 120 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