Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EqualityComparer<T>.Default vs. T.Equals

Suppose I've got a generic MyClass<T> that needs to compare two objects of type <T>. Usually I'd do something like ...

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

Now suppose my MyClass<T> has a constructor that supports passing a custom IEqualityComparer<T>, similar to Dictionary<T>. In that case I'd need to do ...

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

To remove this lengthy if statement, it'd be good if I could have _comparer default to a 'default comparer' if the regular constructor is used. I searched for something like typeof(T).GetDefaultComparer() but wasn't able to find anything like it.

I did find EqualityComparer<T>.Default, could I use that? And would then this snippet ...

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

... provide the same results as using o1.Equals(o2) for all possible cases?

(As a side note, would this mean I'd also need to use any special generic constraint for <T>?)

like image 654
takrl Avatar asked May 02 '11 13:05

takrl


People also ask

What is the default equality Comparer?

Equals(T) for equality, otherwise it will use your Equals(object) method. This method defaults to reference equality as defined in the object class.


2 Answers

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!

like image 197
Daniel Hilgarth Avatar answered Sep 28 '22 08:09

Daniel Hilgarth


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.

like image 31
abatishchev Avatar answered Sep 28 '22 06:09

abatishchev