38

假设我有一个MyClass<T>需要比较两个 type 对象的泛型<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>?)

4

5 回答 5

49

它应该是相同的,但不能保证,因为它取决于 type 的实现细节T
说明:
如果没有对 的约束T,o1.Equals(o2) 将调用Object.Equals,即使T实现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是一个好方法,因为你不需要支持有缺陷的对象!

于 2011-05-02T13:24:37.293 回答
4

默认情况下,直到在类中被覆盖,Object.Equals(a,b)/a.Equals(b)执行引用比较。

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

于 2011-05-02T13:27:06.587 回答
3

是的,我认为使用 是明智的EqualityComparer<T>.Default,因为它使用IEquatable<T>if 类型T实现它的实现,或者覆盖Object.Equalselse 。你可以这样做:

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))
    {
     ...
    }
}
于 2011-05-02T13:29:46.483 回答
2

您可以使用 null coaelescense 运算符 ?? 缩短 if 如果它真的很重要

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

  }
于 2011-05-02T13:27:29.593 回答
2

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

于 2011-05-02T13:29:39.090 回答