Is there a default IEqualityComparer<T>
implementation that uses ReferenceEquals
?
EqualityComparer<T>.Default
uses ObjectComparer, which uses object.Equals()
. In my case, the objects already implement IEquatable<T>
, which I need to ignore and compare by object's reference only.
I thought it was time to update the previous answers implementation to .Net4.0+ where it simplifies by becoming non-generic thanks to contravariance on the
IEqualityComparer<in T>
interface:Now there only needs to exist one instance for all your reference-equality checking instead of one for each type
T
as was the case before.Also you save typing by not having to specify
T
every time you want to use this!To clarify for those who are not familiar with the concepts of Covariance and Contravariance...
...will work just fine. This is not limited to e.g.
HashSet<object>
or similar (in .Net4.0).Also for anyone wondering why
x == y
is reference equality, it is because the==
operator is a static method, which means it is resolved at compile-time, and at compile-time x and y are of typeobject
so here it resolves to the==
operator ofobject
- which is the real reference equality method. (In fact theObject.ReferenceEquals(object, object)
method is simply a redirect to the object equals operator.)Just in case there is no default implementation, this is my own:
Edit by 280Z28: Rationale for using
RuntimeHelpers.GetHashCode(object)
, which many of you probably haven't seen before. :) This method has two effects that make it the correct call for this implementation:ReferenceEquals
works for null parameters, so should the comparer's implementation of GetHashCode().Object.GetHashCode()
non-virtually.ReferenceEquals
specifically ignores any overrides ofEquals
, so the implementation of GetHashCode() should use a special method that matches the effect of ReferenceEquals, which is exactly what RuntimeHelpers.GetHashCode is for.[end 280Z28]
Here's a simple implementation for C# 6.
EDIT (You don't have to read this unless you're interested in the comments below)
@AnorZaken devoted many paragraphs to the three letters of the
new
modifier here. Let's summarise.The single defined instance
Equals(object,object)
method implements theEquals
method of the two declared interfaces for this type,IEqualityComparer
and its generic counterpartIEqualityComparer<object>
. The signatures are identical, so this definition satisfies both interfaces.The instance method
ReferenceEqualityComparer.Equals(object,object)
hides the staticobject.Equals(object,object)
method.Without
new
the compiler warns about this. What does this actually mean?It means that if you want to call the static
object.Equals
methods, you cannot call it on an instance ofReferenceEqualityComparer
. Is this a big deal?No. In fact it's desired behaviour. It means that if you want to call
object.Equals(a,b)
you cannot do it via code such asReferenceEqualityComparer.Default.Equals(a,b)
. That code is clearly requesting reference equality -- no one would reasonably expect it to perform default/value equality. Why wouldn't you just code the more explicitobject.Equals(a,b)
anyway? So the use ofnew
provides sensible and desirable behaviour, and allows compilation with no warnings.How else could you suppress the warning? If you use a
#pragma warning disable 108
/#pragma warning restore 108
then the outcome is the same as usingnew
, except you've added a bunch more noise to your code.new
suffices and explains the intent more clearly to others.Alternatively you could use explicit implementations for the two interface
Equals
methods, but then if you usedReferenceEqualityComparer.Default.Equals(a,b)
you wouldn't have reference equality at all.In reality, hiding static methods with instance methods is rarely a problem because static methods are dereferenced from a type specifier, not an instance specifier. That is, you use
Foo.StaticMethod()
notnew Foo().StaticMethod()
. Calling static methods from instances is unnecessary at best and misleading/incorrect at worst.Further, for equality comparers, you rarely use their concrete types directly. Rather, you use them with APIs such as collections.
So whilst this was an interesting and at times confusing discussion, it was rather fruitless.