Равно vs GetHashCode при сравнении объектов

должны ли мы переопределить оба Equals и GetHashCode свойства при реализации сравнения экземпляров пользовательского класса?

в следующем коде у меня есть коллекция классов. Класс!--5--> по сравнению с ID класс B - by Code.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<I> bars = new List<I>();
            bars.Add(new A() { Id = 1, Code = "one A" });
            bars.Add(new B() { Id = 1, Code = "one B" });
            bars.Add(new A() { Id = 1, Code = "one A+" });
            bars.Add(new B() { Id = 1, Code = "one B" }); // Code = "one B+"

            var distictBars = bars.Distinct();

            foreach (var item in distictBars)
            {
                Debug.WriteLine(item.Code);
            }
        }
    }

    interface I
    {
        string Code { get; set; }
    }

    class A : I, IEquatable<A>
    {
        public int Id { get; set; }
        public string Code { get; set; }

        public bool Equals(A other)
        {
            // this is the ??? comparison
            return this.Id == other.Id;
            //return this.Code == other.Code;
        }

        public override bool Equals(object obj)
        {
            if (obj is A)
                return this.Equals(obj as A);
            else
                return object.ReferenceEquals(this, obj);
        }

        public override int GetHashCode()
        {
            // this is the wanted comparison
            return this.Id;
        }
    }

    class B : I, IEquatable<B>
    {
        public int Id { get; set; }
        public string Code { get; set; }

        public bool Equals(B other)
        {
            // this is the ??? comparison
            return this.Id == other.Id;
        }

        public override bool Equals(object obj)
        {
            if (obj is B)
                return this.Equals(obj as B);
            else
                return object.ReferenceEquals(this, obj);
        }

        public override int GetHashCode()
        {
            // this is the wanted comparison
            return this.Code.GetHashCode();
        }
    }
}

выход:

one A
one B

в случае с комментарием Code = "one B+" выход

one A
one B
one B+

теперь я спрашиваю себя, для чего я должен переопределить Equals на B класс, если кажется, что это не влияет на сравнение?

Is GetHasCode() переопределение, достаточное для такого рода сравнений?

3 ответов


вот что вам нужно понять о взаимоотношениях между Equals и GetHashCode.

хэш-коды используются хэш-таблицами, чтобы быстро найти "ведро", в котором, как ожидается, будет существовать элемент. если элементы находятся в двух разных ведрах, предполагается, что они не могут быть равны.

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

если два объекта имеют то же самое хэш-код, они будут находиться в одном ведре хэш-таблицы. затем их Equals методы будут вызываться для определения равенства.

так GetHashCode должны возвращает одно и то же значение для двух объектов, которые вы хотите считать равными.


на Distinct метод будет использовать GetHashCode метод определения неравенства между элементами, и Equals метод для определения равенства.

сначала сделайте быстрое сравнение, используя хэш-код, чтобы определить, какие элементы определенно не равны, т. е. имеют разные хэш-коды, затем он сравнивает элементы, имеющие один и тот же хэш-код, чтобы определить, какие из них действительно равны.

в реализации B класс у вас непоследовательный реализация GetHashCode и Equals метод, поэтому сравнение не будет работать должным образом. Ваши двое!--3--> объекты имеют разные хэш-коды, поэтому они не будут сравнивать друг с другом. Два элемента, которые считаются равными, также должны возвращать один и тот же хэш-код.

если класс реализует IEquatable<T> интерфейс Equals(T) метод будет использоваться, в противном случае метод.


вам всегда нужно переопределить их вместе и с совместимых реализаций. Совпадение/несоответствие хэш-кода означает (соответственно)"возможно равенство" и "неравенство". Хэш-код сама по себе не указывает на равенство. Следовательно, после того, как найдено совпадение хэш-кода (или используется для создания групп значений),Equals по-прежнему проверяется для определения соответствия.

Если два не согласны, вы можете никогда не найти спички.