Я хочу понять сценарии, в которых IEqualityComparer<T>
и IEquatable<T>
.
Документация MSDN для обоих выглядит очень похоже.
В чем разница между IEqualityComparer <T> и IEquatable <T>?
Ответ 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. Обычно вам нужно использовать только один, а не оба.