C#XNA:字典问题

时间:2010-02-07 23:31:06

标签: c# dictionary xna assert iequatable

我是C#的新手。也许我没有正确实现IEquatable,因为我的类型的对象应该被认为是相同的。

班级:

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用来测试相等性?

更新:根据Thomas的建议,我添加了一个相等函数,现在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;
    }

神秘地说,这个测试仍然失败了:

   [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(4, 1)时,测试也会失败,但当密钥为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()是个问题?

2 个答案:

答案 0 :(得分: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声明为结构而不是类。这样,您甚至不必实现任何内容,因为值类型会根据其字段值进行比较

答案 1 :(得分: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;
    }
}