Подтвердить что ты не робот

Использовать пользовательский объект в качестве словаря

Я хочу использовать пользовательский объект в качестве ключа словаря, в основном, у меня есть что-то вроде этого: (я не могу использовать .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?

Спасибо

4b9b3361

Ответ 1

Вам также необходимо переопределить GetHashCode() (и предпочтительно также Equals()). В противном случае объект возвращает другой хэш-код, а это означает, что ключ не найден при поиске.

Контракт GetHashCode() указывает, что возвращаемое значение из двух объектов ДОЛЖНО быть равным, если два объекта считаются равными. Это корень вашей проблемы; ваш класс не отвечает этому требованию. Контракт не указывает, что значение должно быть иным, если оно не равно, но это улучшит производительность. (Если все объекты возвращают один и тот же хэш-код, вы также можете использовать плоский список с точки зрения производительности.)

Простая реализация в вашем случае может быть:

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

Обратите внимание, что было бы неплохо проверить, есть ли AValue или BValue null. (Это будет несколько сложно, поскольку вы не ограничиваете общие типы A и B, поэтому вы не можете просто сравнивать значения с null - например, типы могут быть типами значений.) 1

Также неплохо сделать классы, которые вы намерены использовать в качестве ключей словаря неизменяемыми. Если вы измените значение объекта, который используется в качестве ключа, словарь будет демонстрировать странное поведение, так как объект теперь находится в ведре, где он не принадлежит.


1 Обратите внимание, что вы можете использовать EqualityComparer<A>.Default.GetHashCode(AValue) (и аналогичный для BValue) здесь, так как это устранит необходимость нулевой проверки.

Ответ 3

Я просто переопределял функцию GetHashCode, но кажется, что даже если GetHashCode возвратил такое же значение, обновления не произошло. Имея Equals, все весело и весело [