繁体   English   中英

平等比较器<t> .Default vs. T.Equals</t>

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM