EqualityComparer<T>.Default vs. T.Equals

会有一股神秘感。 提交于 2019-12-02 20:08:10

It should be the same, but it is not guaranteed, because it depends on implementation details of the type T.
Explanation:
Without a constraint to T, o1.Equals(o2) will call Object.Equals, even if T implements IEquatable<T>.
EqualityComparer<T>.Default however, will use Object.Equals only, if T doesn't implement IEquatable<T>. If it does implement that interface, it uses IEquatable<T>.Equals.
As long as T's implementation of Object.Equals just calls IEquatable<T>.Equals the result is the same. But in the following example, the result is not the same:

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;
    } 
}

Now, it doesn't make any sense to implement a class like this. But you will have the same problem, if the implementer of MyObject simply forgot to override Object.Equals.

Conclusion:
Using EqualityComparer<T>.Default is a good way to go, because you don't need to support buggy objects!

By default, until overridden in a class, Object.Equals(a,b)/a.Equals(b) performs comparison by reference.

What comparer will be returned by EqualityComparer<T>.Default depends on T. For example, if T : IEquatable<> then the appropriate EqualityComparer<T> will be created.

You could use the null coaelescense operator ?? to shorten the if if it really matters

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

  }

Yes, I think it would be wise to use the EqualityComparer<T>.Default, because it uses the implementation of IEquatable<T> if the type T implements it, or the override of Object.Equals otherwise. You could do it as follows:

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))
    {
     ...
    }
}
Ryan Hauert

That's exactly what Dictionary<> and other generic collections in the BCL do if you don't specify a comparer when constructing the object. The benefit of this is that EqualityComparer<T>.Default will return the right comparer for IEquatable<T> types, nullable types, and enums. If T is none of those, it will do a simple Equals comparison like you're old code is doing.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!