使用自定义对象作为词典键

时间:2011-08-09 16:00:12

标签: c#

我想使用自定义对象作为Dictionary键,主要是,我有这样的事情:(我不能使用.net 4.0所以我没有元组)

class Tuple<A, B> : IEquatable<Tuple<A,B>>
{
  public A AValue { get; set; }
  public B BValue { get; set; }

  public Tuple(A a, B b){ AValue = a; BValue = b; }

  public bool Equals(Tuple<A, B> tuple)
  {
    return tuple.AValue.Equals(AValue) && tuple.BValue.Equals(BValue);
  }

  public bool Equals(object o)
  {
  return this.Equals(o as Tuple<A,B>);
  }
}
然后我做了类似的事。

  var boolmap = new Dictionary<Tuple<bool, bool>, string>();
  boolmap.Add(new Tuple<bool,bool>(true, true), "A");
  boolmap.Add(new Tuple<bool,bool>(true, false), "B");
  boolmap.Add(new Tuple<bool,bool>(false, true), "C");
  boolmap.Add(new Tuple<bool,bool>(false, false), "D");
  var str = boolmap[new Tuple<bool,bool>(true, false)];

我在最后一行得到一个KeyNotFound异常。为什么是这样 ?我实现IEquatable是不够的?

由于

3 个答案:

答案 0 :(得分:32)

您还需要覆盖GetHashCode()(最好还是Equals())。你的平等对象返回一个不同的哈希码,这意味着在查找时找不到密钥。

GetHashCode()契约指定当两个对象被认为相等时,两个对象的返回值必须相等。这是你问题的根源;你的班级不符合这个要求。如果它们不相等,则合同不指定该值必须不同,但这将提高性能。 (如果所有对象都返回相同的哈希码,那么您也可以从性能角度使用平面列表。)

您的案例中的简单实现可能是:

public override int GetHashCode()
{
    return AValue.GetHashCode() ^ BValue.GetHashCode();
}

请注意,测试AValueBValuenull可能是个好主意。 (这有点复杂,因为您不限制泛型类型AB,因此您不能只将值与null进行比较 - 类型可以是值类型,例子。) 1

将打算用作字典键的类变为不可变的也是一个好主意。如果更改正用作键的对象的值,则字典将显示奇怪的行为,因为该对象现在位于不属于它的存储桶中。


1 请注意,您可以在此处使用EqualityComparer<A>.Default.GetHashCode(AValue)(和BValue类似),因为这样就无需进行空检查。

答案 1 :(得分:7)

覆盖Equals方法时需要覆盖GetHashCode。可以在这里找到更多解释:

Why is it important to override GetHashCode when Equals method is overridden?

答案 2 :(得分:0)

我刚刚覆盖GetHashCode函数,但似乎即使GetHashCode返回相同的值,更新也没有发生。 拥有Equals,一切都很有趣和嬉戏