EqualityComparer 。默认与T.Equals

假设我有一个通用的MyClass需要比较两个类型的对象。 通常我会做……

 void DoSomething(T o1, T o2) { if(o1.Equals(o2)) { ... } } 

现在假设我的MyClass有一个支持传递自定义IEqualityComparer的构造函数,类似于Dictionary 。 在那种情况下,我需要做……

 private IEqualityComparer _comparer; public MyClass() {} public MyClass(IEqualityComparer 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.Default ,我可以使用吗? 然后这个片段……

 public MyClass() { _comparer = EqualityComparer.Default; } void DoSomething(T o1, T o2) { if(_comparer.Equals(o1, o2)) { ... } } 

…提供与使用o1.Equals(o2)的所有可能情况相同的结果?

(作为旁注,这是否意味着我还需要对使用任何特殊的通用约束?)

它应该是相同的,但不能保证,因为它取决于类型T实现细节。
说明:
如果没有T的约束,o1.Equals(o2)将调用Object.Equals ,即使T实现了IEquatable
EqualityComparer.Default IEquatableIEquatable下,如果T没有实现IEquatable ,则仅使用Object.Equals 。 如果它确实实现了该接口,则它使用IEquatable.Equals
只要TObject.Equals实现只调用IEquatable.Equals ,结果就是一样的。 但在以下示例中,结果不一样:

 public class MyObject : IEquatable { 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.Default是一个很好的方法,因为你不需要支持错误的对象!

默认情况下,在类中重写之前, Object.Equals(a,b) / a.Equals(b)通过引用执行比较。

EqualityComparer.Default将返回什么比较器EqualityComparer.Default取决于T 例如,如果T : IEquatable<>则将创建适当的EqualityComparer

你可以使用null coaelescense运算符?? 缩短if if是否真的重要

  if ((_comparer ?? EqualityComparer.Default).Equals(o1, o2)) { } 

是的,我认为使用EqualityComparer.Default是明智的,因为如果类型T实现它,则使用IEquatable的实现,否则使用Object.Equals的覆盖。 你可以这样做:

 private IEqualityComparer _comparer; public IEqualityComparer Comparer { get { return _comparer?? EqualityComparer.Default;} set { _comparer=value;} } public MyClass(IEqualityComparer comparer) { _comparer = comparer; } void DoSomething(T o1, T o2) { if(Comparer.Equals(o1, o2)) { ... } } 

如果在构造对象时没有指定比较器,这正是BCL中的Dictionary<>和其他generics集合所做的事情。 这样做的好处是EqualityComparer.Default将为IEquatable类型,可空类型和枚举返回正确的比较器。 如果T不是那些,它将进行简单的Equals比较,就像你的旧代码正在做的那样。