EqualityComparer<T>.Default vs. T.Equals
本文关键字:vs Equals Default gt EqualityComparer lt | 更新日期: 2023-09-27 17:54:46
假设我有一个通用的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
默认为'默认比较器',这将是很好的。我搜索了类似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>
使用任何特殊的通用约束?)
应该是相同的,但不能保证,因为它取决于类型T
的实现细节。
解释:
如果没有对T
的约束,01 . equals (o2)将调用Object.Equals
,即使T
实现了IEquatable<T>
。
然而,如果T
没有实现IEquatable<T>
, EqualityComparer<T>.Default
将只使用Object.Equals
。如果它确实实现了该接口,则使用IEquatable<T>.Equals
。
只要T
对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
是一个很好的方法,因为您不需要支持有bug的对象!
默认情况下,Object.Equals(a,b)
/a.Equals(b)
通过引用进行比较,直到在类中被覆盖。
EqualityComparer<T>.Default
返回的比较器取决于T
。例如,如果是T : IEquatable<>
,那么将创建相应的EqualityComparer<T>
。
是的,我认为使用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))
{
...
}
}
您可以使用空对齐操作符??缩短if,如果它真的很重要
if ((_comparer ?? EqualityComparer<T>.Default).Equals(o1, o2))
{
}
如果在构造对象时不指定比较器,那么这正是Dictionary<>
和BCL中的其他泛型集合所做的。这样做的好处是,EqualityComparer<T>.Default
将为IEquatable<T>
类型、可空类型和枚举返回正确的比较器。如果T都不是,它将做一个简单的等号比较,就像你的旧代码所做的那样。