Для сравнения на равенство типа "T" перегрузите эти методы:
int GetHashCode() //Overrides Object.GetHashCode
bool Equals(object other) //Overrides Object.Equals; would correspond to IEquatable, if such an interface existed
bool Equals(T other) //Implements IEquatable<T>; do this for each T you want to compare to
static bool operator ==(T x, T y)
static bool operator !=(T x, T y)
Ваш код сравнения для конкретного типа должен быть сделан в одном месте : тип-безопасный IEquatable<T>
метод интерфейса Equals(T other)
.
Если вы сравниваете с другим типом (T2), внедрите также IEquatable<T2>
и поместите код сравнения полей для этого типа в Equals (T2 other).
Все перегруженные методы и операторы должны перенаправлять задачу сравнения на равенство основному типобезопасному методу экземпляра Equals (T other), чтобы поддерживать чистую иерархию зависимостей и вводить более строгие гарантии на каждом уровне, чтобы исключить избыточность и ненужную сложность .
bool Equals(object other)
{
if (other is T) //replicate this for each IEquatable<T2>, IEquatable<T3>, etc. you may implement
return Equals( (T)other) ); //forward to IEquatable<T> implementation
return false; //other is null or cannot be compared to this instance; therefore it is not equal
}
bool Equals(T other)
{
if ((object)other == null) //cast to object for reference equality comparison, or use object.ReferenceEquals
return false;
//if ((object)other == this) //possible performance boost, ONLY if object instance is frequently compared to itself! otherwise it's just an extra useless check
//return true;
return field1.Equals( other.field1 ) &&
field2.Equals( other.field2 ); //compare type fields to determine equality
}
public static bool operator ==( T x, T y )
{
if ((object)x != null) //cast to object for reference equality comparison, or use object.ReferenceEquals
return x.Equals( y ); //forward to type-safe Equals on non-null instance x
if ((object)y != null)
return false; //x was null, y is not null
return true; //both null
}
public static bool operator !=( T x, T y )
{
if ((object)x != null)
return !x.Equals( y ); //forward to type-safe Equals on non-null instance x
if ((object)y != null)
return true; //x was null, y is not null
return false; //both null
}
Обсуждение:
Предыдущая реализация централизует сравнение с типом (то есть равенство полей) до конца реализации IEquatable<T>
для типа.
Операторы ==
и !=
имеют параллельную, но противоположную реализацию. Я предпочитаю это тому, чтобы иметь одну ссылку на другую, так что есть дополнительный вызов метода для зависимой. Если оператор !=
просто собирается вызвать оператор ==
, а не предлагать оператор с одинаково высокой эффективностью, то вы также можете просто использовать !(obj1 == obj2)
и избежать дополнительного вызова метода.
Сравнение с самим собой исключено из оператора equals и реализаций IEquatable<T>
, поскольку в некоторых случаях оно может привести к 1. ненужным издержкам и / или 2. несовместимой производительности в зависимости от того, как часто экземпляр сравнивается с самим собой по сравнению с другие случаи.
Альтернатива, которую я не люблю, но должен упомянуть, - это отменить эту настройку, централизовав вместо этого код равенства для конкретного типа в операторе равенства, и от этого зависят методы Equals. Затем можно использовать комбинацию ReferenceEquals(obj1,obj2)
, чтобы одновременно проверять равенство ссылок и нулевое равенство, как Филипп упоминал в предыдущем посте, но эта идея вводит в заблуждение. Кажется, что вы убиваете двух зайцев одним выстрелом, но на самом деле вы создаете больше работы - после того, как вы определили, что объекты не являются ни нулевыми, ни одинаковыми экземплярами, вам, кроме того, придется по-прежнему проверять, является ли каждый экземпляр нулевой. В моей реализации вы проверяете, является ли единичный экземпляр пустым ровно один раз. К тому моменту, когда вызывается метод экземпляра Equals, уже исключено, что первый сравниваемый объект является нулевым, поэтому остается только проверить, является ли другой нулевой. Таким образом, после не более чем двух сравнений мы переходим непосредственно к проверке поля, независимо от того, какой метод мы используем (Equals(object),Equals(T),==,!=
). Кроме того, как я уже говорил, если вы действительно сравниваете и возражаете против себя большую часть времени, то вы можете добавить эту проверку в метод Equals непосредственно перед погружением в сравнения полей. Смысл его добавления в последнюю очередь заключается в том, что вы все равно можете поддерживать иерархию потока / зависимостей, не вводя избыточную / бесполезную проверку на каждом уровне.