Какова «лучшая» каноническая реализация Equals () для ссылочных типов?

Реализовать Equals () для ссылочных типов сложнее, чем кажется. Моя текущая каноническая реализация выглядит так:

public bool Equals( MyClass obj )
{
  // If both refer to the same reference they are equal.
  if ( ReferenceEquals( obj, this ) )
    return true;

  // If the other object is null they are not equal because in C# this cannot be null.
  if ( ReferenceEquals( obj, null ) )
   return false;

   // Compare data to evaluate equality    
   return _data.Equals( obj._data );
}

public override bool Equals( object obj )
{
  // If both refer to the same reference they are equal.
  if ( ReferenceEquals( obj, this ) )
    return true;

  // If the other object is null or is of a different types the objects are not equal. 
  if ( ReferenceEquals( obj, null ) || obj.GetType() != GetType() )
    return false;

  // Use type-safe equality comparison
  return Equals( (MyClass)obj );
}

public override int GetHashCode()
{
  // Use data's hash code as our hashcode  
  return _data.GetHashCode();
}

Я думаю, что это касается всех угловых случаев (наследования и т. д.), Но я могу ошибаться. Что вы думаете, ребята?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
1 187
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Это зависит от того, пишете ли вы тип значения или ссылочный тип. Для сортируемого типа значения я рекомендую следующее: Фрагмент кода для Visual Studio 2005, который реализует тип каркасного значения в соответствии с рекомендациями по проектированию платформы.

Я знаю, что реализация для типов значений отличается. Я спрашивал о ссылочных типах.

Eddie Velasquez 16.09.2008 23:04

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

cori 23.04.2012 19:28

@cori, если вы сравните написание ссылки Equals со значением Equals, и вы выяснили, что вы написали значение Equals для ссылки, тогда это может быть неправильно. Так что в этом отношении есть некоторая ценность.

Peter Ritchie 25.04.2012 23:43

Что касается наследования, я думаю, вам следует просто позволить парадигме объектно-ориентированного программирования творить чудеса.

В частности, следует удалить проверку GetType(), это может нарушить полиморфизм в дальнейшем.

Я не согласен. Представьте, что у нас есть классы Apple и Orange, производные от класса Fruit. Если мы удалим проверку GetType () в реализации Equals in Fruit, мы сможем сравнивать яблоки с апельсинами, если каждый производный класс также не переопределяет Equals () должным образом. Это могло очень быстро испортиться.

Eddie Velasquez 16.09.2008 23:12

Я согласен с чакритом, объекты разных типов должны иметь возможность быть семантически равными, если они имеют одинаковые данные или идентификатор.

Лично я использую следующее:

    public override bool Equals(object obj)
    {
        var other = obj as MyClass;
        if (other == null) return false;

        return this.data.Equals(other.data);
    }

Я не согласен с вами. Это позволило бы нам сравнивать яблоки с апельсинами.

Eddie Velasquez 16.09.2008 23:16

Дело в том, что, может быть, вы действительно хотите, чтобы они были равны. Например, проверка GetType не позволяет использовать прокси; у вас будет 2 одинаковых объекта, один из которых заключен в контейнер другого типа, но вы хотите идентифицировать их как одинаковые.

Santiago Palladino 23.09.2008 17:17

@SantiagoPalladino: часто имеет смысл определять компараторы равенства, которые используют более свободное определение эквивалентности, чем Object.Equals. Например, .net определяет различные нечувствительные к регистру средства сравнения строк, которые будут рассматривать «HELLO» как эквивалент «Hello» или «heL10», даже если строки разные. Такие компараторы могут принимать как одинаковые объекты, так и разные типы. Мне очень не нравится понятие объектов, заменяющих Object.Equals для определения отношения эквивалентности, которое является настолько свободным, что позволяет объектам разных типов сравнивать неравные, если только один явно не предусматривает ...

supercat 25.09.2012 02:30

... возможность того, что вызов X.Equals(Y) может потребовать от X вызова Y.Equals(X), если Y является экземпляром более производного типа, чем X. В противном случае, если прокси знает, как сравнивать себя с объектом, но объект не знает, как сравнивать себя с прокси, метод Equals не будет определять правильное отношение повторения (которое, по определению, должно быть рефлексивным).

supercat 25.09.2012 02:33

Лучше надейтесь, что this._data не равно нулю, если это также ссылочный тип.

public bool Equals( MyClass obj )
{
    if (obj == null) {
        return false;
    }
    else {
        return (this._data != null && this._data.Equals( obj._data ))
                         || obj._data == null;
    }
}

public override bool Equals( object obj )
{
    if (obj == null || !(obj is MyClass)) {
        return false;
    }
    else {
        return this.Equals( (MyClass)obj );
    }
}

public override int GetHashCode() {
    return this._data == null ? 0 : this._data.GetHashCode();
}

Ты прав. Это просто «каноническая» реализация, чтобы доказать концепции, лежащие в основе «Equals». Моя «настоящая» реализация обычно реализуется с использованием Equals (a, b) вместо a.Equals (b).

Eddie Velasquez 16.09.2008 23:15
Ответ принят как подходящий

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

http://gregbeech.com/blog/implementing-object-equality-in-dotnet

Моя реализация «общая». Как видите, в конце Equals (Object) есть вызов Equals (MyClass). Мне известно о проблемах изменчивости и GetHashCode (); но это важное наблюдение. Он укусил меня несколько раз. Жаль, что нет "простого" способа объявить "классы" только для чтения.

Eddie Velasquez 18.09.2008 22:35

Другие вопросы по теме