Про порівняння об'єктів за значенням — 6: Structure Equality Implementation

попередній публікації ми розглянули особливості пристрою і роботи структур платформи .NET є "типами за значенням" (Value Types) в розрізі порівняння за значенням об'єктів — примірників структур.
Тепер розглянемо готовий приклад реалізації порівняння за значенням об'єктів — примірників структур.
Допоможе приклад для структур більш точно визначити з предметної (доменної) точки зору область застосовності порівняння об'єктів за значенням в цілому, і тим самим спростити зразок порівняння за значенням об'єктів — примірників класів є ссылочными типами (Reference Types) виведений в одній з попередніх публікацій?
Читати далі →

Про порівняння об'єктів за значенням — 5: Structure Equality Problematic

попередній публікації ми вивели найбільш повний і коректний спосіб реалізації порівняння за значенням об'єктів — примірників класівссылочными типами — Reference Types для платформи .NET.

Яким чином потрібно модифікувати запропонований спосіб для коректної реалізації порівняння за значенням об'єктів — примірників структур"типами за значенням" — Value Types)?

Примірники структур, в силу своєї природи, завжди порівнюються за значенням.
Для визначених типів, таких як Boolean або Int32, під порівнянням за значенням розуміється порівняння безпосередньо значень примірників структур.
Якщо структура визначена розробником — користувачем платформи, то порівняння за замовчуванням автоматично реалізується як порівняння значень полів примірників структур.
(Подробиці див. в описі методу ValueType.Equals(Object) та операторів == і !=
Також при цьому автоматично певним чином реалізується метод ValueType.GetHashCode(), перекриває метод Object.GetHashCode().

І в цьому випадку є кілька суттєвих підводних каменів:


Читати далі →

Про порівняння об'єктів за значенням — 4, або Inheritance & Equality operators

попередній публікації ми отримали варіант реалізації порівняння об'єктів за значенням для платформи .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), і т. д.
Таким чином, клієнтський код може порівнювати об'єкти будь-яким способом — результат порівняння буде детермінований.

Однак, вимагає розкриття питання:

Як саме забезпечується детермінованість результату при реалізації статичних методів і операторів порівняння в разі спадкування — з урахуванням того, що статичні методи і оператори не мають поліморфним поведінкою.
Читати далі →

Про порівняння об'єктів за значенням — 3, або Type-specific Equals & Equality operators

ми розглянули коректну реалізацію мінімально необхідного набору доробок класу для порівняння об'єктів класу за значенням.
Тепер розглянемо 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-сумісні мови підтримують оператори або їх перевантаження).
Розглянемо реалізацію порівняння об'єктів за значенням з урахуванням вищевикладених умов, на прикладі класу Person.
Відразу наведемо остаточний варіант коду з поясненнями, чому це зроблено саме так, і як саме це працює.
(Демонстрація виведення рішення з урахуванням кожного нюансу містить занадто багато ітерацій.)
Читати далі →

Про порівняння об'єктів за значенням — 2, або Особливості реалізації методу Equals

попередній публікації ми розглянули загальні принципи реалізації мінімально необхідних доробок класу для можливості порівняння об'єктів класу за значенням з допомогою стандартної інфраструктури платформи .NET.

Ці доробки включають перекриття методів Object.Equals(Object), Object.GetHashCode().

Зупинимося докладніше на особливостях реалізації методу Object.Equals(Object) для відповідності наступного вимогу документації:

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

// та, як наслідок, наступного:
If (x.Equals(y) && y.Equals(z)) повертає true, then x.Equals(z) повертає true.

Клас 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. Перевіримо, чи дає це коректний результат.

Читати далі →