我是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()
是个问题?
答案 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;
}
}