我有一个与此类似的课程:
public class Int16_2D
{
public Int16 a, b;
public override bool Equals(Object other)
{
return other is Int16_2D &&
a == ((Int16_2D)other).a &&
b == ((Int16_2D)other).b;
}
}
这适用于HashSet<Int16_2D>
。但是在Dictionary<Int16_2D, myType>
中,.ContainsKey
在不应该返回false时返回false。我在==
的实施中遗漏了什么?
答案 0 :(得分:3)
要使类在哈希表或字典中工作,您需要实现GetHashCode()
!我不知道为什么它在HashSet中工作;我猜这只是运气。
请注意使用可变字段计算Equals或GetHashCode()是危险的。为什么?考虑一下:
var x = new Int16_2D { a = 1, b = 2 };
var set = new HashSet<Int16_2D> { x };
var y = new Int16_2D { a = 1, b = 2 };
Console.WriteLine(set.Contains(y)); // True
x.a = 3;
Console.WriteLine(set.Contains(y)); // False
Console.WriteLine(set.Contains(x)); // Also false!
换句话说,当您设置x.a = 3;
时,您正在更改x的哈希码。但是x在哈希表中的位置是基于其旧哈希码,因此x现在基本上丢失了。请参阅http://ideone.com/QQw08
另外,正如svick所说,实施Equals
并未实现==
。如果您未实施==
,==
运算符将提供参考比较,因此:
var x = new Int16_2d { a = 1, b = 2 };
var y = new Int16_2d { a = 1, b = 2 };
Console.WriteLine(x.Equals(y)); //True
Console.WriteLine(x == y); //False
总之,你最好把它变成一个不变的类型;因为它只有4个字节长,所以我可能会把它变成一个不可变的结构。
答案 1 :(得分:2)
您需要覆盖GetHashCode()
。与HashSet<T>
一起使用的事实可能只是一个幸运的巧合。
两个集合都使用从GetHashCode
获取的哈希码来查找应该放置对象的存储桶(即对象列表)。然后它搜索该存储桶以查找对象,并使用Equals
来确保相等。这就是Dictionary和HashSet的快速查找属性。但是,这也意味着,如果GetHashCode
未被覆盖以使其对应于类型Equals
方法,则您将无法在其中一个集合中找到此类对象。
您应该,几乎总是同时实施GetHashCode
和Equals
,或者不执行任何一项。
答案 2 :(得分:0)
您还需要覆盖GetHashCode
以使字典生效。
答案 3 :(得分:0)
你也必须覆盖GetHashCode()
- 这与覆盖Equals
密切相关。字典正在使用GetHashCode()
来确定值将落入哪个bin中 - 只有在该bin中找到合适的项目时,它才会检查项目的实际相等性。