Яка різниця між IEquatable і просто переважаючим Object.Equals ()?


185

Я хочу, щоб мій Foodклас міг перевірити, коли він дорівнює іншому екземпляру Food. Пізніше я буду використовувати його проти списку, і я хочу використовувати його List.Contains()метод. Чи варто впроваджувати IEquatable<Food>або просто переосмислювати Object.Equals()? Від MSDN:

Цей метод визначає рівність, використовуючи порівняльник рівності за замовчуванням, визначений реалізацією об'єктом методу IEquatable.Equals для T (тип значень у списку).

Отже, наступне моє запитання: які функції / класи рамки .NET використовують Object.Equals()? Чи варто його використовувати в першу чергу?


3
Дуже вдале пояснення тут blogs.msdn.com/b/jaredpar/archive/2009/01/15/…
nawfal

можливий дублікат Understanding IEquatable
nawfal

Відповіді:


214

Основна причина - продуктивність. Коли дженериків були введені в .NET 2.0 вони були в стані додати купу акуратних класів , таких як List<T>, Dictionary<K,V>, HashSet<T>і т.д. Ці структури широко використовують GetHashCodeі Equals. Але для типів цінності це вимагає боксу. IEquatable<T>дозволяє структурі реалізувати сильно набраний Equalsметод, тому бокс не потрібен. Таким чином, набагато краща ефективність при використанні типів значень із загальними колекціями.

Типи посилань не отримують великої користі, але IEquatable<T>реалізація дозволяє уникнути трансляції, System.Objectяка може змінити ситуацію, якщо її часто називають.

Як зазначається в блозі Джареда Парсона , ви все одно повинні реалізувати переоцінки об'єкта.


Чи є між цитатами посилання? Я завжди думав, що касти - це лише "заяви", які ви робите компілятору, коли ви присвоюєте кілька не очевидних записів з одного виду об'єкта в інший. Це означає, що після того, як ви компілюєте, код навіть не знає, що там був склад.
пожирав елізіум

7
Це вірно в мовах C ++, але не .NET, які забезпечують безпеку типу. Існує амплітуда виконання, і якщо акторський склад не вдається, викидається виняток. Отже, за плату за кастинг існує невеликий штраф за час виконання. Компілятор може оптимізувати подальше оновлення Наприклад, об'єкт o = (об'єкт) "рядок"; Але downcasting - рядок s = (string) o; - має відбуватися під час виконання.
Джош

1
Я бачу. Випадково у вас є місце, де я можу отримати таку "глибшу" інформацію про .NET? Дякую!
пожирав елізіум

7
Я б рекомендував CLR через C # від Джеффа Ріхтера та C # в глибині від Джона Скіта. Що стосується блогів, блоги Wintellect хороші, блоги msdn тощо.
Josh

Чи робить IEquatable<T>інтерфейс чимось більшим, ніж нагадує розробнику включити public bool Equals(T other) члена до класу чи структури? Наявність або відсутність інтерфейсу не суттєво впливає на час виконання. EqualsЗдається, що перевантаження є всім необхідним.
mikemay

48

За даними MSDN :

Якщо ви реалізуєте IEquatable<T>, вам слід також переосмислити реалізацію базового класу Object.Equals(Object)та GetHashCode таким чином, щоб їх поведінка відповідала IEquatable<T>.Equals методиці. Якщо ви переопрацюєте Object.Equals(Object), ваша переосмислена реалізація також викликається у викликах статичного Equals(System.Object, System.Object)методу вашого класу. Це забезпечує, що всі виклики Equalsметоду повертають послідовні результати.

Тож здається, що між цими двома немає реальної функціональної різниці, крім того, що їх можна було б назвати залежно від того, як використовується клас. З точки зору продуктивності, краще використовувати загальну версію, оскільки з нею не пов’язані штрафи за бокс / розблокування.

З логічної точки зору, також краще реалізувати інтерфейс. Переопределення об'єкта насправді нікому не говорить про те, що ваш клас насправді є вирівняним. Переосмислення може бути просто класом "робити нічого" або дрібною реалізацією. Використовуючи інтерфейс, прямо сказано: "Ей, ця річ справедлива для перевірки рівності!" Це просто кращий дизайн.


9
Структури обов'язково повинні реалізовувати iEquatable (їхнього власного типу), якщо вони будуть використовуватись як ключі у словнику чи подібній колекції; це запропонує значне підвищення продуктивності. Ненасліджувані класи отримають незначне підвищення продуктивності, впровадивши IEquatable (їхнього власного типу). Спадкові класи повинні // не // впроваджувати IEquatable.
supercat

30

Розширюючи те, що Джош сказав на практичному прикладі. +1 до Джоша - я збирався написати те саме у своїй відповіді.

public abstract class EntityBase : IEquatable<EntityBase>
{
    public EntityBase() { }

    #region IEquatable<EntityBase> Members

    public bool Equals(EntityBase other)
    {
        //Generic implementation of equality using reflection on derived class instance.
        return true;
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as EntityBase);
    }

    #endregion
}

public class Author : EntityBase
{
    public Author() { }
}

public class Book : EntityBase
{
    public Book() { }
}

Таким чином, у мене є метод повторного використання Equals (), який працює з вікна для всіх моїх похідних класів.


Ще одне питання. Яка перевага використання "obj як EntityBase" замість (EntityBase) obj? Просто питання стилю чи взагалі є якась перевага?
пожирав елізіум

22
у випадку "obj як EntityBase" - якщо obj не типу EntityBase, він передасть "null" і продовжить без будь-яких помилок і винятків, але у випадку "obj" (EntityBase) ", він буде намагатися передати obj до EntityBase, і якщо obj не буде типу EntityBase, він кине InvalidCastException. І так, "як" можна застосувати лише до посилальних типів.
це. __curious_geek

1
Посилання Джоша на блог Джареда Пар, начебто, говорить про те, що вам також потрібно перекрити GetHashCode. Хіба це не так?
Полюбовно

3
Я не отримую додаткової цінності, яку надає ваша реалізація. Чи можете ви уточнити проблему, яку вирішує ваш абстрактний базовий клас?
Мерт Аккакая

1
@Amicable - так, щоразу, коли ви перекриватимете Object.Equals (Object), ви також повинні перекрити GetHashCode, щоб контейнери працювали.
Намфорд

0

Якщо ми зателефонуємо object.Equals, це примушує дорогий бокс за типовими цінами. Це небажано в сценаріях, залежних від продуктивності. Рішення - використовувати IEquatable<T>.

public interface IEquatable<T>
{
  bool Equals (T other);
}

Ідея IEquatable<T>полягає в тому, що вона дає такий же результат, як object.Equalsі швидше. Обмеження where T : IEquatable<T>повинно використовуватися із загальними типами, як показано нижче.

public class Test<T> where T : IEquatable<T>
{
  public bool IsEqual (T a, T b)
  {
    return a.Equals (b); // No boxing with generic T
  }
}

в іншому випадку вона пов'язується з slower object.Equals().

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.