В чем разница между IEqualityComparer <T>и IEquatable <T>? - PullRequest
136 голосов
/ 16 февраля 2012

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

Ответы [ 5 ]

104 голосов
/ 16 февраля 2012

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

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

59 голосов
/ 25 января 2013

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

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

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

  • С другой стороны, если есть несколько одинаково разумных методов сравнения двух T s на равенство, 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>, вы можете просто заменить ее, если она недостаточно хороша для ваших целей.

23 голосов
/ 06 декабря 2012

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

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

class Person : IEqualityComparer<Person>

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

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

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

10 голосов
/ 16 февраля 2012

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

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

4 голосов
/ 16 февраля 2012

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

...