[英]EqualityComparer<T>.Default vs. T.Equals
假设我有一个通用的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
的约束, o1.Equals(o2) 将调用Object.Equals
,即使T
实现IEquatable<T>
。
EqualityComparer<T>.Default
但是,如果T
没有实现IEquatable<T>
,则仅使用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;
}
}
现在,像这样实现 class 没有任何意义。 但是,如果MyObject
的实现者只是忘记覆盖Object.Equals
,您将遇到同样的问题。
结论:
使用EqualityComparer<T>.Default
是 go 的好方法,因为您不需要支持有缺陷的对象!
默认情况下,除非在 class、 Object.Equals(a,b)
/ a.Equals(b)
中被覆盖,否则按引用执行比较。
EqualityComparer<T>.Default
将返回什么比较器取决于T
。 例如,如果T: IEquatable<>
则将创建相应的EqualityComparer<T>
。
您可以使用 null coaelescense 运算符? 缩短 if 如果它真的很重要
if ((_comparer ?? EqualityComparer<T>.Default).Equals(o1, o2))
{
}
如果您在构造 object 时未指定比较器,这正是Dictionary<>
和 BCL 中的其他通用 collections 所做的。 这样做的好处是EqualityComparer<T>.Default
将为IEquatable<T>
类型、可为空的类型和枚举返回正确的比较器。 如果 T 不是这些,它将像您的旧代码一样进行简单的 Equals 比较。
是的,我认为使用EqualityComparer<T>.Default
是明智的,因为如果类型T
实现它,它使用IEquatable<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))
{
...
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.