Pull to refresh

О сравнении объектов по значению — 1: Beginning

Reading time7 min
Views27K

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


По умолчанию два объекта считаются равными, если соответствующие переменные содержат одну и ту же ссылку. В противном случае объекты считаются неравными.

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

Пусть есть класс Person, содержащий персональные данные — имя, фамилию, и дату рождения персоны.


На примере этого класса рассмотрим:

  1. минимально необходимый набор доработок класса для того, чтобы объекты этого класса сравнивались по значению с помощью стандартной инфраструктуры .NET;
  2. минимально необходимый и достаточный набор доработок, чтобы объекты этого класса всегда сравнивались по значению с помощью стандартной инфраструктуры .NET — если явно не указано, что сравнение должно производиться по ссылке.

Для каждого случая рассмотрим, каким именно образом лучше реализовать сравнение объектов по значению, чтобы получился согласованный и, насколько это возможно, компактный, copy-paste free, производительный код.

Задача является не настолько тривиальной, насколько это может показаться на первый взгляд.

А также рассмотрим, какие улучшения могли бы быть внесены в платформу, чтобы упростить реализацию этой задачи.
Читать дальше →
Total votes 24: ↑17 and ↓7+10
Comments58

О сравнении объектов по значению — 2, или Особенности реализации метода Equals

Reading time9 min
Views12K
В предыдущей публикации мы рассмотрели общие принципы реализации минимально необходимых доработок класса для возможности сравнения объектов класса по значению с помощью стандартной инфраструктуры платформы .NET.

Эти доработки включают перекрытие методов Object.Equals(Object) и Object.GetHashCode().

Остановимся подробнее на особенностях реализации метода Object.Equals(Object) для соответствия следующему требованию в документации:

x.Equals(y) returns the same value as y.Equals(x).

Класс Person, созданный в предыдущей публикации, содержит следующую реализацию метода Equals(Object):

Person.Equals(Object)
public override bool Equals(object obj)
{
    if ((object)this == obj)
        return true;

    var other = obj as Person;

    if ((object)other == null)
        return false;

    return EqualsHelper(this, other);
}

После проверки ссылочного равенства текущего и входящего объекта, в случае отрицательного результата проверки, происходит приведение входящего объекта к типу Person для возможности сравнения объектов по значению.

В соответствии с примером, приведенным в документации, приведение производится с помощью оператора as. Проверим, дает ли это корректный результат.
Читать дальше →
Total votes 21: ↑16 and ↓5+11
Comments13

О сравнении объектов по значению — 3, или Type-specific Equals & Equality operators

Reading time7 min
Views6.5K

Ранее мы рассмотрели корректную реализацию минимально необходимого набора доработок класса для сравнения объектов класса по значению.


Теперь рассмотрим Type-specific реализацию сравнения объектов по значению, включающую реализацию Generic-интерфейса IEquatable(Of T) и перегрузку операторов "==" и "!=".


Type-specific сравнение объектов по значению позволяет достичь:


  • Более стабильного, масштабируемого и мнемонического (читаемого) кода (последнее за счет перегруженных операторов).
  • Более высокой производительности.

Кроме того, реализация Type-specific сравнения по значению необходима по причинам:


  • Стандартные Generic-коллекции (List(Ot T), Dictionary(Of TKey, TValue) и др.) рекомендуют наличие реализации IEquatable(Of T) для всех объектов, помещаемых в коллекции.
  • Стандартный компаратор EqualityComparer(Of T).Default использует (по умолчанию — при наличии) реализацию IEquatable(Of T) у операндов.

Реализация одновременно всех способов сравнения сопряжена определенными с трудностями, т.к. для корректной работы требуется обеспечить:


  • Соответствие результатов сравнения у различных способов (включая сохранение соответствия при наследовании).
  • Минимизацию copy-paste и общего объема кода.
  • Учет того, что операторы сравнения технически являются статическими методами и, соответственно, у них отсутствует полиморфность (а также, что не все CLS-совместимые языки поддерживают операторы или их перегрузку).

Рассмотрим Type-specific реализацию сравнения объектов по значению с учетом вышеизложенных условий, на примере класса Person.


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

Читать дальше →
Total votes 23: ↑18 and ↓5+13
Comments13

О сравнении объектов по значению — 4, или Inheritance & Equality operators

Reading time6 min
Views7.3K

В предыдущей публикации мы получили вариант реализации сравнения объектов по значению для платформы .NET, на примере класса Person, включающий:


  • перекрытие методов Object.GetHashCode(), Object.Equals(Object);
  • реализацию интерфейса IEquatable (Of T);
  • реализацию Type-specific статических метода Equals(Person, Person) и операторов ==(Person, Person), !=(Person, Person).

Каждый из способов сравнения для любой одной и той же пары объектов возвращает один и тот же результат:


Пример кода
Person p1 = new Person("John", "Smith", new DateTime(1990, 1, 1));
Person p2 = new Person("John", "Smith", new DateTime(1990, 1, 1));
//Person p2 = new Person("Robert", "Smith", new DateTime(1991, 1, 1));

object o1 = p1;
object o2 = p2;

bool isSamePerson;

isSamePerson = o1.Equals(o2);
isSamePerson = p1.Equals(p2);
isSamePerson = object.Equals(o1, o2);
isSamePerson = Person.Equals(p1, p2);
isSamePerson = p1 == p2;
isSamePerson = !(p1 == p2);

При этом, каждый из способов сравнения является коммутативным:
x.Equals(y) возвращает тот же результат, что и y.Equals(x), и т.д.


Таким образом, клиентский код может сравнивать объекты любым способом — результат сравнения будет детерминирован.


Однако, требует раскрытия вопрос:


Как именно обеспечивается детерминированность результата при реализации статических методов и операторов сравнения в случае наследования — с учетом того, что статические методы и операторы не обладают полиморфным поведением.

Читать дальше →
Total votes 11: ↑8 and ↓3+5
Comments7

О сравнении объектов по значению — 5: Structure Equality Problematic

Reading time4 min
Views10K

В предыдущей публикации мы вывели наиболее полный и корректный способ реализации сравнения по значению объектов — экземпляров классов (являющихся ссылочными типами — Reference Types) для платформы .NET.


Каким образом нужно модифицировать предложенный способ для корректной реализации сравнения по значению объектов — экземпляров структур (являющихся "типами по значению" — Value Types)?


Экземпляры структур, в силу своей природы, всегда сравниваются по значению.


Для предопределенных типов, таких как Boolean или Int32, под сравнением по значению понимается сравнение непосредственно значений экземпляров структур.


Если структура определена разработчиком — пользователем платформы (User defined struct), то сравнение по умолчанию автоматически реализуется как сравнение значений полей экземпляров структур. (Подробности см. в описании метода ValueType.Equals(Object) и операторов == и !=). Также при этом автоматически определенным образом реализуется метод ValueType.GetHashCode(), перекрывающий метод Object.GetHashCode().


И в этом случае есть несколько существенных подводных камней:

Читать дальше →
Total votes 14: ↑13 and ↓1+12
Comments40

О сравнении объектов по значению — 6: Structure Equality Implementation

Reading time5 min
Views7K

В предыдущей публикации мы рассмотрели особенности устройства и работы структур платформы .NET, являющихся "типами по значению" (Value Types) в разрезе сравнения по значению объектов — экземпляров структур.


Теперь рассмотрим готовый пример реализации сравнения по значению объектов — экземпляров структур.


Поможет ли пример для структур более точно определить с предметной (доменной) точки зрения область применимости сравнения объектов по значению в целом, и тем самым упростить образец сравнения по значению объектов — экземпляров классов, являющихся ссылочными типами (Reference Types), выведенный в одной из предыдущих публикаций?

Читать дальше →
Total votes 14: ↑12 and ↓2+10
Comments15

Переопределение Equals и GetHashCode. А оно надо?

Reading time8 min
Views25K
Если вы знакомы с C#, то, скорее всего, знаете, что необходимо всегда переопределять Equals, а также GetHashCode, чтобы избежать снижения производительности. Но что будет, если этого не сделать? Сегодня сравним производительность при двух вариантах настройки и рассмотрим инструменты, помогающие избегать ошибок.

Читать дальше →
Total votes 19: ↑18 and ↓1+17
Comments10

Асинхронное программирование на C#: как дела с производительностью?

Reading time10 min
Views25K
Совсем недавно мы уже рассказывали о том, нужно ли переопределять Equals и GetHashCode при программировании на C#. Сегодня мы разберемся с параметрами производительности асинхронных методов. Присоединяйтесь!

Читать дальше →
Total votes 18: ↑18 and ↓0+18
Comments6

C#: один сценарий использования для любых задач

Reading time10 min
Views16K
Привет, Хабр! Мы продолжаем рассказывать об асинхронном программировании на C#. Сегодня поговорим о едином сценарии использования или пользовательском сценарии, подходящем для любых задач в рамках асинхронного программирования. Затронем темы синхронизации, взаимоблокировок, настройки операторов, обработки исключений и много другого. Присоединяйтесь!

Читать дальше →
Total votes 13: ↑13 and ↓0+13
Comments12