Вправа ілюструє низку властивостей рівності в C#:
Object.Equals()== і !=. Це вважають ідіоматичнішим, ніж використовувати метод Equals(), який також доступний для цих типів. Програмістам на Java варто памʼятати, що під час роботи з рядками тексту == у C# порівнює за значенням, а в Java, до якої вони можуть повернутися, за посиланням.Equals(), успадкованого від object. Якщо мета перевірки на рівність - переконатися, що два обʼєкти є одним і тим самим екземпляром, то достатньо покластися на реалізацію object. Якщо ні, потрібно перевизначити object.Equals().Equals() потрібно перевизначити.Equals() міститиме перевірки на рівність для членів простих типів за допомогою ==, а для членів типів-посилань викликатиме Equals() рекурсивно.class StatusBar
{
private readonly int width = 200, height = 20;
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return width == (other as StatusBar).width && height == (other as StatusBar).height;
}
}
class Window
{
private readonly string title = "Main";
private readonly StatusBar statusBar = new StatusBar();
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return title == (other as Window).title && statusBar.Equals((other as Window).statusBar);
}
}
object.ReferenceEquals() використовують, щоб порівняти два обʼєкти й визначити, чи це той самий екземпляр. Це додає ясності й буває необхідним там, де Equals() і == перевизначено чи перевантажено.var winA = new Window(); // above code shows that all windows are equal
var winB = new Window();
ReferenceEquals(winA, winB);
// => false
var winC = winA;
ReferenceEquals(winA, winC);
// => true
public override bool Equals(object obj), середовища розробки (IDE) зазвичай генерують перевантаження protected bool Equals(FacialFeatures other) для використання, коли задіяне успадкування. Похідний клас може викликати Equals() базового класу, а потім додати власну перевірку.==, якщо оператор == і метод Equals() у класі не перевантажені (див. вправу operator-overloading) або якщо важливо лише те, що посилання рівні.struct розглядаються у вправі structs.tuple розглядається у вправі tuples.protected bool Equals(T other)
{
return field1 == other.field1 && field2.Equals(other.field2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((T) obj);
}
null і обʼєктів неправильного типу та досить добре оптимізований.SequenceEquals(), але без LINQ доведеться перебрати обидві колекції та порівняти елементи по черзі.== і != з власними класами, див. вправу operator-overloading.Object.GetHashCode()object.GetHashCode() повертає хеш-код у вигляді 32-бітного цілого числа. Хеш-код використовують класи словників і множин, як-от Dictionary<T> і HashSet<T>, щоб зберігати й діставати обʼєкти з хорошою продуктивністю. У випадку словників хешування стосується ключів.Equals() має супроводжуватися перевизначенням GetHashCode(). Між Equals() і GetHashCode() існує звʼязок, який має зберігатися для правильної роботи класів словників, хеш-множин і будь-яких інших, що використовують хеш-код. Метод потрібно реалізувати так, щоб не залишати пасток для супровідників, які згодом можуть додати колекцію на основі хеш-коду.Equal() повертає правду), то GetHashCode() для цих двох обʼєктів має повертати однакове значення. У зворотному напрямку це не працює. Цей звʼязок несиметричний. Уявімо функцію пошуку, яка спершу за хеш-кодом звертається до «кошика», а потім вибирає конкретний елемент за допомогою перевірки на рівність.HashCode.Combine(), передавши значення, використані в перевірці на рівність (або їх підмножину). Памʼятаймо: що більше інформації передано в Combine(), то продуктивнішою, ймовірно, буде реалізація хешу.public class Assessment
{
private int rating;
private Person boss;
public override int GetHashCode()
{
return HashCode.Combine(rating, boss);
}
}
Щоб трохи підвищити продуктивність, особливо там, де обʼєкти належать до колекцій, можна додати перевантажений член public bool Equals(T other).
Це заощадить певний обсяг перевірок типу для типів-посилань і крок упакування для типів-значень, адже їх не доведеться перетворювати на обʼєкт (упаковувати) як аргумент для public override bool Equals(object other).
Якщо додати до класу інтерфейс IEquatable<T>, це вимагатиме реалізації перевантаження. Якщо код не містить процедур, які приймають обʼєкти типу IEquatable<T> (і, ймовірно, роблять щось цікаве щодо рівності незалежно від класу, що його реалізує), то жодної іншої вагомої причини додавати цей інтерфейс немає.
IEqualityComparer<T>Якщо клас можна унікально ідентифікувати двома різними способами, скажімо, клас Person, що має SSID і унікальну адресу електронної пошти, то .NET дає змогу двом різним колекціям використовувати різні перевірки хеш-коду та рівності. Кожна може мати власну реалізацію IEqualityComparer<T> зі своїми методами Equals() і GetHashCode(). Можна мати словник із ключем за SSID і ще один із ключем за адресою електронної пошти.
Там, де задіяно IEqualityComparer<T>, зазвичай усе одно варто перевизначити Equals() і GetHashCode() у власному класі елемента, щоб уникнути проблем поза межами класів колекцій.
Одне з міркувань під час використання IEqualityComparer<T> - те, що приватні методи тощо не будуть доступні для перевірки на рівність.
Якщо задіяно лише одну хешовану колекцію, то, можливо, краще уникнути IEqualityComparer<T> і інкапсулювати рівність і хеш-код у самому обʼєкті. Уже сама по собі ситуація, коли компілятор не може забезпечити таку ключову залежність, як між обʼєктом і хешованою колекцією, не є ідеальною, але якщо логіка хешування й порівняння лежить деінде, супровіднику буде ще легше змінювати члени класу, не зважаючи на наслідки для колекції.
Один із примітивів, який може спантеличити необачного програміста, - це перевірка рівності чисел з плаваючою комою. Про це йдеться в документі about.md для концепції floating-point-numbers.
У цій статті показано деякі рішення, які доводиться ухвалювати щодо рівності та успадкування. Вони стосуватимуться лише тих, хто працює з екземплярами класу й похідного класу та має перевіряти рівність між ними.