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>
?)
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 onT
. For example, ifT : IEquatable<>
then the appropriateEqualityComparer<T>
will be created.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 callObject.Equals
, even ifT
implementsIEquatable<T>
.EqualityComparer<T>.Default
however, will useObject.Equals
only, ifT
doesn't implementIEquatable<T>
. If it does implement that interface, it usesIEquatable<T>.Equals
.As long as
T
's implementation ofObject.Equals
just callsIEquatable<T>.Equals
the result is the same. But in the following example, the result is not the same: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 overrideObject.Equals
.Conclusion:
Using
EqualityComparer<T>.Default
is a good way to go, because you don't need to support buggy objects!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 thatEqualityComparer<T>.Default
will return the right comparer forIEquatable<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.Yes, I think it would be wise to use the
EqualityComparer<T>.Default
, because it uses the implementation ofIEquatable<T>
if the typeT
implements it, or the override ofObject.Equals
otherwise. You could do it as follows:You could use the null coaelescense operator ?? to shorten the if if it really matters