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>
?)
Equals(T) for equality, otherwise it will use your Equals(object) method. This method defaults to reference equality as defined in the object class.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With