EqualityComparer.По умолчанию против T. равно
Предположим, у меня есть универсальный MyClass<T>
что нужно сравнить два объекта типа <T>
. Обычно я делаю что-то подобное ...
void DoSomething(T o1, T o2)
{
if(o1.Equals(o2))
{
...
}
}
теперь предположим, что моя MyClass<T>
имеет конструктор, который поддерживает передачу пользовательской IEqualityComparer<T>
, как Dictionary<T>
. В таком случае мне придется это сделать ...
private IEqualityComparer<T> _comparer;
public MyClass() {}
public MyClass(IEqualityComparer<T> comparer)
{
_comparer = comparer;
}
void DoSomething(T o1, T o2)
{
if((_comparer != null && _comparer.Equals(o1, o2)) || (o1.Equals(o2)))
{
...
}
}
чтобы удалить это длинное заявление if, было бы хорошо, если бы я мог _comparer
по умолчанию используется "default comparer", если используется обычный конструктор. Я искал что-то вроде typeof(T).GetDefaultComparer()
но не смог найти ничего подобного.
я нашел EqualityComparer<T>.Default
, могу я это использовать? И будет тогда этот фрагмент ...
public MyClass()
{
_comparer = EqualityComparer<T>.Default;
}
void DoSomething(T o1, T o2)
{
if(_comparer.Equals(o1, o2))
{
...
}
}
... предоставьте те же результаты, что и при использовании o1.Equals(o2)
для всех возможных случаев?
(в качестве примечания, будет ли это означать, что мне также нужно использовать любое специальное общее ограничение для <T>
?)
5 ответов
это должно быть то же самое, но это не гарантируется, потому что это зависит от деталей реализации типа T
.
Объяснение:
Без ограничения T
, o1.Equals (o2) вызовет Object.Equals
, даже если T
осуществляет IEquatable<T>
.EqualityComparer<T>.Default
однако, будет использовать Object.Equals
только, если T
не выполнять IEquatable<T>
. Если это тут реализовать этот интерфейс, он использует IEquatable<T>.Equals
.
Пока С Object.Equals
просто звонки IEquatable<T>.Equals
результат тот же. Но в следующем примере результат не тот же самый:
public class MyObject : IEquatable<MyObject>
{
public int ID {get;set;}
public string Name {get;set;}
public override bool Equals(object o)
{
var other = o as MyObject;
return other == null ? false : other.ID == ID;
}
public bool Equals(MyObject o)
{
return o.Name == Name;
}
}
теперь нет никакого смысла реализовывать такой класс. Но у вас будет такая же проблема, если исполнитель MyObject
просто забыл переопределить Object.Equals
.
вывод:
Используя EqualityComparer<T>.Default
это хороший способ пойти, потому что вам не нужно поддерживать багги объекты!
по умолчанию, до переопределения в классе,Object.Equals(a,b)
/a.Equals(b)
выполняет сравнение по ссылке.
какой компаратор будет возвращен EqualityComparer<T>.Default
зависит от T
. Например, если T : IEquatable<>
затем EqualityComparer<T>
будет создан.
вы можете использовать оператор null coaelescense ?? чтобы сократить если это действительно имеет значение
if ((_comparer ?? EqualityComparer<T>.Default).Equals(o1, o2))
{
}
Да, я думаю, было бы разумно использовать EqualityComparer<T>.Default
, потому что он использует реализацию IEquatable<T>
Если тип T
реализует его, или переопределить Object.Equals
иначе. Вы могли бы сделать это следующим образом:
private IEqualityComparer<T> _comparer;
public IEqualityComparer<T> Comparer
{
get { return _comparer?? EqualityComparer<T>.Default;}
set { _comparer=value;}
}
public MyClass(IEqualityComparer<T> comparer)
{
_comparer = comparer;
}
void DoSomething(T o1, T o2)
{
if(Comparer.Equals(o1, o2))
{
...
}
}
именно так Dictionary<>
и другие общие коллекции в BCL делают, если вы не указываете компаратор при построении объекта. Преимущество этого в том, что EqualityComparer<T>.Default
вернет правильный компаратор для IEquatable<T>
типы, типы с нулевым значением и перечисления. Если T не является ни одним из них, он будет делать простое сравнение равных, как вы делаете старый код.