В чем разница между IEqualityComparer <T> и IEquatable <T>?

Я хочу понять сценарии, в которых IEqualityComparer<T> и IEquatable<T>. Документация MSDN для обоих выглядит очень похоже.

Ответ 1

IEqualityComparer<T> - это интерфейс для объекта, который выполняет сравнение на двух объектах типа T.

IEquatable<T> предназначен для объекта типа T, чтобы он мог сравнивать себя с другим.

Ответ 2

При принятии решения о том, следует ли использовать IEquatable<T> или IEqualityComparer<T>, можно спросить:

Есть ли предпочтительный способ тестирования двух экземпляров T для равенства или существует несколько одинаково допустимых способов?

  • Если существует один способ тестирования двух экземпляров T для равенства или если один из нескольких методов является предпочтительным, то IEquatable<T> будет правильным выбором: этот интерфейс предполагается реализовать только самой T, так что один экземпляр T имеет внутреннее знание того, как сравнивать себя с другим экземпляром T.

  • С другой стороны, если существует несколько одинаково разумных методов сравнения двух T для равенства, IEqualityComparer<T> будет казаться более подходящим: этот интерфейс не предназначен для реализации самим T, но другими "внешними" классами. Поэтому при тестировании двух экземпляров T для равенства, поскольку T не имеет внутреннего понимания равенства, вам нужно будет сделать явный выбор экземпляра IEqualityComparer<T>, который выполняет тест в соответствии с вашими конкретными требованиями.

Пример:

Рассмотрим эти два типа (которые должны иметь семантика значений):

interface IIntPoint : IEquatable<IIntPoint>
{
    int X { get; }
    int Y { get; }
}

interface IDoublePoint  // does not inherit IEquatable<IDoublePoint>; see below.
{
    double X { get; }
    double Y { get; }
}

Почему только один из этих типов наследует IEquatable<>, но не другой?

В теории существует только один разумный способ сравнения двух экземпляров любого типа: они равны, если свойства X и Y в обоих случаях равны. Согласно этому мнению, оба типа должны реализовывать IEquatable<>, потому что не представляется вероятным, что существуют другие осмысленные способы выполнения теста на равенство.

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

sealed class DoublePointNearEqualityComparerByTolerance : IEqualityComparer<IDoublePoint>
{
    public DoublePointNearEqualityComparerByTolerance(double tolerance) { … }
    …
    public bool Equals(IDoublePoint a, IDoublePoint b)
    {
        return Math.Abs(a.X - b.X) <= tolerance  &&  Math.Abs(a.Y - b.Y) <= tolerance;
    }
    …
}

Обратите внимание, что страница, с которой я связан (выше), явно указывает, что этот тест для почти равенства имеет некоторые недостатки. Поскольку это реализация IEqualityComparer<T>, вы можете просто заменить ее, если она недостаточно хороша для ваших целей.

Ответ 3

У вас уже есть базовое определение , что они. Короче говоря, если вы реализуете IEquatable<T> в классе T, метод Equals объекта типа T сообщает вам, является ли сам объект (тот, который проверен на равенство) другому экземпляру того же тип T. Принимая во внимание, что IEqualityComparer<T> предназначен для проверки равенства любых двух экземпляров T, обычно выходящих за пределы экземпляров T.

Что касается , то для чего они предназначены для. Из определения должно быть ясно, что поэтому IEquatable<T> (определенный в самом классе T) должен быть стандартом де-факто для представления уникальности его объектов/экземпляров. HashSet<T>, Dictionary<T, U> (рассматривая GetHashCode тоже), Contains на List<T> и т.д. используют это. Реализация IEqualityComparer<T> on T не помогает вышеупомянутым общим случаям. Впоследствии для реализации IEquatable<T> для любого другого класса, отличного от T, мало значения. Это:

class MyClass : IEquatable<T>

редко имеет смысл.

С другой стороны,

class T : IEquatable<T>
{
    //override ==, !=, GetHashCode and non generic Equals as well

    public bool Equals(T other)
    {
        //....
    }
}

как это сделать.

IEqualityComparer<T> может быть полезна, когда вам требуется выборочная проверка равенства, но не как общее правило. Например, в классе Person в какой-то момент вам может потребоваться проверить равенство двух людей в зависимости от их возраста. В этом случае вы можете:

class Person
{
    public int Age;
}

class AgeEqualityTester : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Age == y.Age;
    }

    public int GetHashCode(Person obj)
    {
        return obj.Age.GetHashCode;
    }
}

Чтобы протестировать их, попробуйте

var people = new Person[] { new Person { age = 23 } };
Person p = new Person() { age = 23 };

print people.Contains(p); //false;
print people.Contains(p, new AgeEqualityTester()); //true

Аналогично IEqualityComparer<T> on T не имеет смысла.

class Person : IEqualityComparer<Person>

Правда это работает, но не выглядит хорошо для глаз и побеждает логику.

Обычно вам нужно IEquatable<T>. Также в идеале вы можете иметь только один IEquatable<T>, тогда как несколько IEqualityComparer<T> возможны на основе разных критериев.

IEqualityComparer<T> и IEquatable<T> в точности аналогичны Comparer<T> и IComparable<T>, которые используются для целей сравнения, а не для приравнивания; хорошая нить здесь, где я написал тот же ответ:)

Ответ 4

IEqualityComparer предназначен для использования, когда выполняется равенство двух объектов, например, если вы хотите определить сравнитель для двух типов, для которых у вас не было источника, или для случаев, когда равенство между двумя вещами имеет смысл только в ограниченном контексте.

IEquatable предназначен для самого объекта (который сравнивается для равенства).

Ответ 5

Один сравнивает два T s. Другой может сравниться с другими T s. Обычно вам нужно использовать только один, а не оба.