IEqualityComparer<T> that uses ReferenceEquals

C#.NetIequalitycomparerReferenceequals

C# Problem Overview


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.

C# Solutions


Solution 1 - C#

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:

  1. It returns 0 when the object is null. Since ReferenceEquals works for null parameters, so should the comparer's implementation of GetHashCode().
  2. It calls 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
}

Solution 2 - C#

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>.

Solution 3 - C#

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 which ensures it is only usable with reference types:

public sealed class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    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);
}

Solution 4 - C#

Solution 5 - C#

Microsoft provide ObjectReferenceEqualityComparer in System.Data.Entity.Infrastructure. Just use ObjectReferenceEqualityComparer.Default as comparer.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYuri AstrakhanView Question on Stackoverflow
Solution 1 - C#Yuri AstrakhanView Answer on Stackoverflow
Solution 2 - C#AnorZakenView Answer on Stackoverflow
Solution 3 - C#Drew NoakesView Answer on Stackoverflow
Solution 4 - C#Patrick from NDepend teamView Answer on Stackoverflow
Solution 5 - C#renouveView Answer on Stackoverflow