Я хочу мой Food
класс, чтобы смочь протестировать каждый раз, когда это равно другому экземпляру Food
. Я буду позже использовать его против Списка, и я хочу использовать List.Contains()
метод. Если я реализую IEquatable<Food>
или просто переопределение Object.Equals()
? Из MSDN:
Этот метод определяет равенство при помощи компаратора равенства по умолчанию, как определено реализацией объекта IEquatable. Равняется методу для T (тип значений в списке).
Таким образом, мой следующий вопрос: который используют функции/классы платформы.NET Object.Equals()
? Я должен использовать его во-первых?
Основная причина - производительность. Когда в .NET 2.0 были представлены дженерики, они смогли добавить кучу изящных классов, таких как List
, Dictionary
, HashSet
и т. Д. Эти структуры интенсивно используют GetHashCode
и Equals
. Но для типов значений это требовало бокса. IEquatable
позволяет структуре реализовать строго типизированный метод Equals
, поэтому упаковка не требуется. Таким образом, производительность намного выше при использовании типов значений с универсальными коллекциями.
Ссылочные типы не так сильно выигрывают, но реализация IEquatable
позволяет избежать преобразования из System.Object
, которое может иметь значение, если он часто вызывается.
Однако, как отмечалось в блоге Джареда Парсона , вы все равно должны реализовать переопределения объектов.
Согласно MSDN:
Если вы реализуете
IEquatable
, вы вы также должны переопределить базовый класс реализацииObject.Equals(Object)
andGetHashCode
так, чтобы их поведение соответствовало с поведениемIEquatable
метод. Если вы переопределите.Equals Object.Equals(Object)
, ваша переопределенная реализация также вызывается в вызовах к статическомуEquals(System.Object, System.Object)
метода вашего класса. Это гарантирует, что все вызовы методаEquals
возвращают согласованные результаты.
Таким образом, кажется, что между этими двумя методами нет реальной функциональной разницы, за исключением того, что любой из них может быть вызван в зависимости от того, как используется класс. С точки зрения производительности, лучше использовать общую версию, потому что с ней не связаны штрафы за боксирование/небоксирование.
С точки зрения логики, также лучше реализовать интерфейс. Переопределение объекта не говорит никому о том, что ваш класс действительно эквивалентен. Переопределение может быть просто классом "ничего не делать" или неглубокой реализацией. Использование интерфейса явно говорит: "Эй, эта вещь действительна для проверки равенства!". Это просто лучший дизайн.
Расширяя сказанное Джошем практическим примером. +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(), который работает из коробки для всех моих производных классов.