EqualityComparer <t> .Default vs. T.Equals </t>

时间:2011-05-02 13:19:21

标签: c# .net generics comparison iequalitycomparer

假设我有一个通用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 个答案:

答案 0 :(得分:39)

它应该是相同的,但不能保证,因为它取决于T类型的实现细节。
说明:
如果没有T的约束,即使Object.Equals实施T,o1.Equals(o2)也会调用IEquatable<T>EqualityComparer<T>.Default但是,如果Object.Equals未实现T,则仅使用IEquatable<T>。如果 实现该界面,则使用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是一个很好的方法,因为你不需要支持错误的对象!

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

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

  if ((_comparer ?? EqualityComparer<T>.Default).Equals(o1, o2))
  {

  }

答案 3 :(得分:2)

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

答案 4 :(得分:2)

是的,我认为使用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)) { ... } } 的实现} 除此以外。你可以这样做:

{{1}}