Должен быть IEquatable <T> Equals() реализован через IComparable <T> CompareTo()?

Я искал ответ в Интернете, но все, что я нашел, было:

Изменить: добавлены некоторые элементы в ответ на ответы

  • Для IEquatable

    • Я должен перегружать Equals(), GetHashCode(), == и != вместе.
    • Я должен уменьшить избыточность через реализацию != через ==.
    • Я должен запечатать класс
  • Для IComparable

    • Я должен перегружать Equals(), GetHashCode(), <, >, <= и >= вместе.
    • На самом деле рекомендуется реализовать IEquatable при этом
    • Перегрузите нестандартную версию IComparable
    • CompareTo() == 0 должен означать Equals() == true

Итак, я думал об этом:

public bool Equals(T other)
{
    if ((object)other == null)
    {
        return false;
    }

    return CompareTo(other) == 0;
}

Я что-то пропускаю или это нормально?

Ответ 1

По словам Эрика Липперта, бывшего разработчика команды компилятора С# в Microsoft:

  • Есть девять способов сделать сравнение в С#: < <= > >= == != object.Equals(object) IEquatable<T>.Equals(T) IComparable<T>.CompareTo(T)
  • В идеале все они должны соответствовать друг другу. То есть, если x == y истинно, то x < y является ложным, но x <= y и x.Equals(y) являются истинными, а x.CompareTo(y) равно нулю и т.д.

Итак, по его мнению, "в идеале" x.CompareTo(y) == 0 подразумевает x.Equals(y) == true и наоборот.

Затем Eric предоставляет пример, реализующий все, используя частный вспомогательный метод:

public int CompareTo(Natural x) { return CompareTo(this, x); }
public static bool operator <(Natural x, Natural y) { return CompareTo(x, y) < 0; }
public static bool operator >(Natural x, Natural y) { return CompareTo(x, y) > 0; }
public static bool operator <=(Natural x, Natural y) { return CompareTo(x, y) <= 0; }
public static bool operator >=(Natural x, Natural y) { return CompareTo(x, y) >= 0; }
public static bool operator ==(Natural x, Natural y) { return CompareTo(x, y) == 0; }
public static bool operator !=(Natural x, Natural y) { return CompareTo(x, y) != 0; } 
public override bool Equals(object obj) { return CompareTo(this, obj as Natural) == 0; }
public bool Equals(Natural x) { return CompareTo(this, x) == 0; }

private static int CompareTo(Natural x, Natural y) { ... }

Ответ 2

x.CompareTo(y) == 0 не означает x.Equals(y) == true.

x.CompareTo(y) == 0 означает только, что при сортировке элементов x и y элементы отображаются в той же позиции

Например, если задан следующий класс:

public class Person
{
    public string Name { get; set;}
    public string Passport { get; set; }
}

var me = new Person { Name = "Diogo Castro", Passport = "12345" };
var someoneElse = new Person { Name = "Diogo Castro", Passport = "67890" }; 

При сортировке этих двух лиц по их имени (в алфавитном порядке) вам нужно me.CompareTo(someoneElse) вернуть 0, так как они имеют одно и то же имя. Поэтому порядок, в котором они появляются, не имеет значения.

Однако вы хотите, чтобы me.Equals(someoneElse) возвращал false, потому что они не являются тем же человеком.

Ответ 3

Согласно примечанию в интерфейсе IEquatable (T), IEquatable интерфейс должен использоваться для определения того, два объекта равны или нет. Согласно Интерфейс IComparable (T) для поддержки сравнения следует использовать IComparable. объектов, чтобы определить их порядок.

Итак, вы можете реализовать свой метод Equals так же, как вы, но при этом вы создаете зависимость между двумя разными способами. Равенство двух объектов и их порядок.

Это всегда зависит от того, как вы смотрите на сам объект. Это может быть (глупый пример, я знаю), что вы указываете, что две машины одинаковы, если они имеют одинаковый engine type, такой же engine power и так далее. Если вы хотите заказать их, вы хотите заказать их, возможно, сравнивая только мощность двигателя, а не тип и все остальное. Таким образом, вы можете реализовать Equals, чтобы вернуть значение true, если сам объект содержит одинаковые значения/ссылки и CompareTo, если они могут иметь только одну и ту же мощность, но, возможно, разные типы двигателей и т.д.

(Sidenote: Вот почему я предпочитаю реализовать сравнение с Интерфейсом IComparer (T).)