В чем разница между IEquatable и просто переопределением Object.Equals ()? - PullRequest
156 голосов
/ 29 апреля 2010

Я хочу, чтобы мой класс Food мог тестировать всякий раз, когда он равен другому экземпляру Food. Позже я буду использовать его против List, и я хочу использовать его List.Contains() метод. Должен ли я реализовать IEquatable<Food> или просто переопределить Object.Equals()? Из MSDN:

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

Итак, мой следующий вопрос: какие функции / классы .NET Framework используют Object.Equals()? Должен ли я использовать его в первую очередь?

Ответы [ 3 ]

185 голосов
/ 29 апреля 2010

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

Ссылочные типы не приносят такой большой пользы, но реализация IEquatable<T> позволяет вам избежать приведения из System.Object, который может иметь значение, если он вызывается часто.

Как отмечено в блоге Джареда Парсона , вы все равно должны реализовать переопределения объектов.

43 голосов
/ 29 апреля 2010

Согласно MSDN :

Если вы реализуете IEquatable(T), вы также следует переопределить базовый класс реализации Object.Equals(Object) и GetHashCode так что их поведение соответствует с этим из IEquatable(T).Equals метод. Если вы переопределите Object.Equals(Object), ваш переопределен реализация также вызывается в вызовах к статическому методу Equals(System.Object, System.Object) в вашем классе. Это гарантирует, что все вызовы Equals метод возвращает непротиворечивый Результаты.

Таким образом, похоже, что между ними нет реальной функциональной разницы, за исключением того, что любой из них может быть вызван в зависимости от того, как используется класс. С точки зрения производительности, лучше использовать универсальную версию, потому что с ней не взимается штраф за упаковку / распаковку.

С логической точки зрения также лучше реализовать интерфейс. Переопределение объекта на самом деле никому не говорит о том, что ваш класс действительно уравновешенный. Переопределение может быть просто классом бездействия или поверхностной реализацией. Использование интерфейса явно говорит: «Эй, это действительно для проверки на равенство!» Это просто лучший дизайн.

26 голосов
/ 29 апреля 2010

Расширяя сказанное Джошем на практическом примере. +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 () многократного использования, который работает из коробки для всех моих производных классов.

...