Равно как GetHashCode при сравнении объектов - PullRequest
10 голосов
/ 12 мая 2011

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

В следующем коде у меня есть коллекция классов. Класс A сравнивается по ID, класс B - по 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, если кажется, что это не влияет на сравнение?

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

Ответы [ 3 ]

15 голосов
/ 12 мая 2011

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

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

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

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

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

6 голосов
/ 12 мая 2011

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

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

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

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

5 голосов
/ 12 мая 2011

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

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

...