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.
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 of Equals
, 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]
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
/// <summary>
/// A generic object comparerer that would only use object's reference,
/// ignoring any <see cref="IEquatable{T}"/> or <see cref="object.Equals(object)"/> overrides.
/// </summary>
public class ObjectReferenceEqualityComparer<T> : EqualityComparer<T>
where T : class
{
private static IEqualityComparer<T> _defaultComparer;
public new static IEqualityComparer<T> Default
{
get { return _defaultComparer ?? (_defaultComparer = new ObjectReferenceEqualityComparer<T>()); }
}
#region IEqualityComparer<T> Members
public override bool Equals(T x, T y)
{
return ReferenceEquals(x, y);
}
public override int GetHashCode(T obj)
{
return RuntimeHelpers.GetHashCode(obj);
}
#endregion
}
I thought it was time to update the previous answers implementation to .Net4.0+ where generics are no longer needed thanks to contravariance on the IEqualityComparer<in T>
interface:
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
public sealed class ReferenceEqualityComparer
: IEqualityComparer, IEqualityComparer<object>
{
private ReferenceEqualityComparer() { }
public static readonly ReferenceEqualityComparer Default
= new ReferenceEqualityComparer();
public /*new*/ bool Equals(object x, object y)
{
return x == y; // This is reference equality! (See explanation below)
}
public int GetHashCode(object obj)
{
return RuntimeHelpers.GetHashCode(obj);
}
}
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.
You no longer have to specify T
every time you want to use this and also avoid polluting with unnecessary generic runtime types.
As for 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 the x
and y
arguments are of type object
.
In fact this is what the Object.ReferenceEquals(object, object)
method source code looks like:
public static bool ReferenceEquals(object objA, object objB) {
return objA == objB;
}
To clarify for those who are not familiar with the concepts of Covariance and Contravariance...
class MyClass
{
ISet<MyClass> setOfMyClass = new HashSet<MyClass>(ReferenceEqualityComparer.Default);
}
...the above code compiles; Notice that it does not say HashSet<object>
.
In .NET 5.0 you now have System.Collections.Generic.ReferenceEqualityComparer
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