# V3229. The 'GetHashCode' method may return different hash codes for equal objects. It uses an object reference to generate a hash for a variable. Check the implementation of the 'Equals' method.

Анализатор обнаружил, что результат `GetHashCode` зависит от хэш-кода ссылки на некоторый объект. При этом результат `Equals` зависит от логики сравнения этого объекта, отличной от сравнения двух ссылок. Это может привести к нарушению контракта: два одинаковых объекта согласно методу `Equals` должны также иметь одинаковые хэш-коды.

Нарушение контракта методов `GetHashCode` и `Equals` может привести к некорректной работе некоторых функций и структур данных:

- коллекции вроде `HashSet` и `Dictionary`;
- LINQ-методы, такие как `Union`, `Intersect` и пр.

Рассмотрим пример:

    public HashSet<Value> Values { get; private set; }  = new();
    ....
    public override bool Equals(object obj)
    {
      if (obj is not CustomObject other)
        return false;

      return Values.SequenceEqual(other.Values);
    }

    public override int GetHashCode()
    {
      return Values.GetHashCode();
    }

В качестве возвращаемого значения `GetHashCode` некоторого класса используется хэш-код от ссылки на коллекцию `Values`. При этом в методе `Equals` происходит поэлементное сравнение с этой коллекцией с помощью `SequenceEqual`.

Способ N1. Упростить сравнение объектов:

    public HashSet<Value> Values { get; private set; } = new();
    ....
    public override bool Equals(object obj)
    {
      if (obj is not CustomObject other)
        return false;

      return Values.Equals(other.Values);
    }

    public override int GetHashCode()
    {
      return Values.GetHashCode();
    }

Способ N2. Алгоритм в `GetHashCode` также должен учитывать хэши объектов коллекции:

    public readonly HashSet<Value> Values { get; private set; } = new();
    ....
    public override bool Equals(object obj)
    {
      if (obj is not CustomObject other)
        return false;

      return Values.SequenceEqual(other.Values);
    }

    public override int GetHashCode()
    {
      return Values.Aggregate(0,
               (hash, val) =>   (hash * 397)
                              ^ (val?.GetHashCode() ?? 0));
    }

Вызов `Aggregate` формирует общий хэш-код элементов коллекции путём их перебора и обновления значения `hash` на каждой итерации согласно функции, переданной вторым аргументом.

Примечание. Необходимо также учитывать, что к методу `GetHashCode` предъявляются требования по скорости работы, а способ N2 может нарушить их.