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