使用自定义对象作为词典键
我想使用自定义对象作为Dictionary键,主要是,我有这样的事情:(我不能使用.net 4.0所以我没有元组)
class Tuple : IEquatable<Tuple> { public A AValue { get; set; } public B BValue { get; set; } public Tuple(A a, B b){ AValue = a; BValue = b; } public bool Equals(Tuple tuple) { return tuple.AValue.Equals(AValue) && tuple.BValue.Equals(BValue); } public bool Equals(object o) { return this.Equals(o as Tuple); } }
然后我做这样的事情。
var boolmap = new Dictionary<Tuple, string>(); boolmap.Add(new Tuple(true, true), "A"); boolmap.Add(new Tuple(true, false), "B"); boolmap.Add(new Tuple(false, true), "C"); boolmap.Add(new Tuple(false, false), "D"); var str = boolmap[new Tuple(true, false)];
我在最后一行得到一个KeyNotFoundexception。 为什么是这样 ? 我实现IEquatable是不够的?
谢谢
您还需要覆盖GetHashCode()
(最好也是Equals()
)。 您的其他对象正在返回不同的哈希码,这意味着在查找时找不到该键。
GetHashCode()
契约指定当两个对象被认为相等时,两个对象的返回值必须相等。 这是你问题的根源; 你的class级不符合这个要求。 合同没有规定如果它们不相等则值必须不同,但这将提高性能。 (如果所有对象都返回相同的哈希码,那么您也可以从性能角度使用平面列表。)
在您的情况下,一个简单的实现可能是:
public override int GetHashCode() { return AValue.GetHashCode() ^ BValue.GetHashCode(); }
请注意,测试AValue
或BValue
是否为null
可能是个好主意。 (这有点复杂,因为您不限制generics类型A
和B
,因此您不能仅将值与null
进行比较 – 例如,类型可以是值类型。) 1
将您打算用作字典键的类创建为不可变的也是一个好主意。 如果更改正用作键的对象的值,则字典将显示奇怪的行为,因为该对象现在位于不属于它的存储桶中。
1请注意,您可以在此处使用EqualityComparer.Default.GetHashCode(AValue)
(以及类似于BValue
),因为这将消除对空检查的需要。
重写Equals方法时需要覆盖GetHashCode
。 可以在这里找到更多解释:
为什么在重写Equals方法时重写GetHashCode很重要?
我只是重写了GetHashCode
函数,但似乎即使GetHashCode
返回相同的值,也没有发生更新。 拥有Equals
,一切都很有趣和嬉闹