IEqualityComparer<T> that uses ReferenceEquals
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:
- It returns 0 when the object is null. Since
ReferenceEquals
works for null parameters, so should the comparer's implementation of GetHashCode(). - It calls
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]
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 it simplifies by becoming non-generic 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>
{
public static readonly ReferenceEqualityComparer Default
= new ReferenceEqualityComparer(); // JIT-lazy is sufficiently lazy imo.
private ReferenceEqualityComparer() { } // <-- A matter of opinion / style.
public 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.
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...
class MyClass
{
ISet<MyClass> setOfMyClass = new HashSet<MyClass>(ReferenceEqualityComparer.Default);
}
...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 type object
so here it resolves to the ==
operator of object
- which is the real reference equality method. (In fact the Object.ReferenceEquals(object, object)
method is simply a redirect to the object equals operator.)
Here's a simple implementation for C# 6 and later:
public sealed class ReferenceEqualityComparer : IEqualityComparer, IEqualityComparer<object>
{
public static ReferenceEqualityComparer Default { get; } = new ReferenceEqualityComparer();
public new bool Equals(object x, object y) => ReferenceEquals(x, y);
public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj);
}
Or a generic version:
public sealed class ReferenceEqualityComparer<T> : IEqualityComparer<T>
{
public static IEqualityComparer<T> Default { get; } = new ReferenceEqualityComparer<T>();
public bool Equals(T x, T y) => ReferenceEquals(x, y);
public int GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj);
}