- От простого к сложному: выполнять сравнение начиная с простых условий и заканчивая более сложными.
- Понять равенство/неравенство объектов как можно раньше: делайте незамедлительный "return true/false".
Реализация шаблона на языке C# в комментариях на примере класса Foo:
class Foo : IEquatable<Foo>
{
public override bool Equals(object obj)
{
Foo foo = obj as Foo;
return this.Equals(foo);
}
public bool Equals(Foo other)
{
// От ПРОСТОГО к СЛОЖНОМУ.
// Выполняйте сравнение начиная с
// простых условий (проверка на null, сравнение ссылок)
// и заканчивая более сложными (сравнение содержимого).
if (other == null)
{
// this заведомо не null.
return false;
}
if (Object.ReferenceEquals(this, other))
{
// обе ссылки ссылаются на один и тот же объект.
return true;
}
// Сравнение содержимого.
// Здесь опишите конкретное сравнения по полям.
//
// От ПРОСТОГО к СЛОЖНОМУ.
// 1. Выделите самые простые относительно
// операции сравнения поля и сравните их первыми.
// Более сложные оставьте на конец.
//
// 2. Делайте незамедлительный "return false"
// при первом же несовпадении.
//
// 3. Сравнивайте поля класса методом Equals,
// за исключением простых типов: field.Equals(other.field)
//
// 4. Не забывайте в конце вызвать base.Equals(other).
return true;
}
}
Ссылки:
Несколько раз сталкивался с ситуацией, когда необходимо было сделать кастомное сравнивание.
ОтветитьУдалитьДо конца не понял поведение framework, но далеко не всегда среда вызывает метод Equals, чаще всего мне приходилось переписывать метод GetHash(), потому что именно он вызывался при сравнении объектов.
В частности, такое поведение было замечено у DependencyObject при методе List.Containts(item)
Если GetHashCode коды совпадают (а они могут совпадать даже у различных(!) объектов, так называемая коллизия), то среда вызывает Equals для дальнейшего сравнения. Equals однозначно определяет равенство.
ОтветитьУдалитьВообще-то если переопределяется Equals, то нужно переопределять и GetHashCode, что у одинаковых объектов были одинаковые хеш-коды.
ОтветитьУдалитьЯ в GetHashCode XOR'ом объединял хеш-коды всех полей, котоыре я использовал в сравнени, возможно, конечно, это не лучший вариант.
Иначе, например, объект нельзя будет использовать в качестве ключа хеш-таблицы.
Решарпер автоматизирует эту рутину :)
ОтветитьУдалитьПредлагаю первые два if в реализации метода public bool Equals(Foo other) заменить на
ОтветитьУдалитьretutn base.Equal(other).
Как вам?
Иван, когда по вашему в таком случае будет выполняться сравнение содержимого объектов?
ОтветитьУдалить@Иван
ОтветитьУдалитьДа, можно и неявно проверять равенство ссылок через:
if(!base.Equal(other))
return fasle;
Но если Foo непосредственный наследник от Object, то мне кажется, что лучше явно проверить ссылки и не делать лишний вызов base.Equals.
Причем для reference-типов можно смело применять это, а для value-типов нужно быть осторожнее, не забывая про boxing, unboxing.
@Виктор Чистов
ОтветитьУдалить@Илья Дубаденко
Согласен с Вами, написал не подумав
Хотел добавить по пункту 3.
ОтветитьУдалитьДля полей объекта, если они ссылочные лучше писать - Object.Equals(this.field,other.field). Так как field у нашего объекта может быть null и field.Equals(other.field) выдаст исключение.
Сергей, спасибо за поправку, "слепой" вызов Equals может привести к NullReferenceException. Кстати стоит отметить, что Object.Equals(null, null) дает True.
ОтветитьУдалитьhttp://stackoverflow.com/questions/1451454/c-how-does-the-static-object-equals-check-for-equality