Переопределить Equals и GetHashCode в классе с одним полем

У меня есть класс:

public abstract class AbstractDictionaryObject
    {
        public virtual int LangId { get; set; }

        public override bool Equals(object obj)
        {
            if (obj == null || obj.GetType() != GetType())
            {
                return false;
            }

            AbstractDictionaryObject other = (AbstractDictionaryObject)obj;
            if (other.LangId != LangId)
            {
                return false;
            }

            return true;
        }

        public override int GetHashCode()
        {
            int hashCode = 0;               
            hashCode = 19 * hashCode + LangId.GetHashCode();
            return hashCode;
        }

И у меня есть производные классы:

public class Derived1:AbstractDictionaryObject
{...}

public class Derived2:AbstractDictionaryObject
{...}

В AbstractDictionaryObject есть только одно общее поле: LangId.
Я думаю, этого недостаточно для перегрузки методов (правильно).
Как я могу идентифицировать объекты?


person user348173    schedule 10.08.2011    source источник


Ответы (1)


Во-первых, вы можете упростить оба своих метода:

 public override bool Equals(object obj)
 {
     if (obj == null || obj.GetType() != GetType())
     {
         return false;
     }

     AbstractDictionaryObject other = (AbstractDictionaryObject)obj;
     return other.LangId == LangId;
 }

 public override int GetHashCode()
 {
     return LangId;
 }

Но в этот момент все должно быть хорошо. Если два производных класса имеют другие поля, они должны сами переопределить GetHashCode и Equals, сначала вызвав base.Equals или base.GetHashCode, а затем применив собственную логику.

Два экземпляра Derived1 с одним и тем же LangId будут эквивалентны в отношении AbstractDictionaryObject, как и два экземпляра Derived2, но они будут отличаться друг от друга, поскольку имеют разные типы.

Если вы хотите дать им разные хэш-коды, вы можете изменить GetHashCode() на:

 public override int GetHashCode()
 {
     int hash = 17;
     hash = hash * 31 + GetType().GetHashCode();
     hash = hash * 31 + LangId;
     return hash;
 }

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

person Jon Skeet    schedule 10.08.2011
comment
Два экземпляра Derived1 с одинаковым LangId будут эквивалентны — это плохо для моей ситуации. Могу ли я ввести поле GUID? Потому что я не хочу переопределять производные классы. - person user348173; 10.08.2011
comment
@user348173: user348173: Если вы хотите, чтобы каждый экземпляр не был равен любому другому экземпляру, то зачем вы вообще переопределяете Equals и GetHashCode? Непонятно, чего вы пытаетесь достичь. Когда два разных объекта следует считать равными? - person Jon Skeet; 10.08.2011
comment
они равны, если langId равен и другие поля, существующие в Derived1, равны. - person user348173; 10.08.2011
comment
например, у меня есть два экземпляра Derived1: Class1 и Class2. Поле LangId одинаковое, но другое поле (например - Name) другое. Так что Class1 и Class2 не должны быть равны. Но я не хочу переопределять Equals и GetHashCode в Derived1. - person user348173; 10.08.2011
comment
@ user348173: Но вы должны переопределить Equals и GetHashCode в Derived1. Вы могли бы потенциально использовать отражение для проверки полей, но это было бы медленно и очень ненадежно — вы могли бы захотеть ввести поле, которое не является частью соглашения о равенстве в Производный1. По сути, базовый класс не может предсказать, какие аспекты класса являются частью его идеи равенства, поэтому вам необходимо обеспечить такое поведение в каждом производном классе. Почему вы не хотите переопределять GetHashCode/Equals в Derived1? - person Jon Skeet; 10.08.2011
comment
@user348173 user348173 Тогда вам придется переопределить Equals и GetHashCode в Derived1. База не знает о полях, которые вы добавляете в производные классы. - person Alexey Ivanov; 10.08.2011
comment
@Jon Skeet: Изначально у меня не было класса AbstractDictionaryObject. И каждый класс реализует Equals и GetHashCode. Я хотел сделать это только один раз, в базовом классе. Поэтому я создал класс AbstractDictionaryObject. - person user348173; 10.08.2011
comment
@ user348173: Но похоже, что у каждого класса есть свои поля и, следовательно, своя собственная идея равенства, поэтому вы не можете поместить эту логику в одно место. Потенциально вы могли бы написать вспомогательные методы для передачи соответствующей информации базовому классу, но в основном вы должны передать идею того, что этот набор информации составляет ключ равенства каким-то образом от производного класса к базовый класс. - person Jon Skeet; 10.08.2011