18

There are several places in BCL where one can make use of IEqualityComparer. Like Enumerable.Contains or Dictionary Constructor. I can provide my comparer if I'm not happy with the default one.

Sometimes I want to know whether the collection contains that very object that I have reference to. Not the one that is considered "equal" in any other meaning.
The question is: whether there exists standard equality comparer in the BCL that relies only on ReferenceEquals method?

The one that I wrote myself is this:

class ReferenceComparer<T> : IEqualityComparer<T> where T : class
{
    private static ReferenceComparer<T> m_instance;

    public static ReferenceComparer<T> Instance
    {
        get
        {
            return m_instance ?? (m_instance = new ReferenceComparer<T>());
        }
    }

    public bool Equals(T x, T y)
    {
        return ReferenceEquals(x, y);
    }

    public int GetHashCode(T obj)
    {
        return RuntimeHelpers.GetHashCode(obj);
    }
}

I didn't test it thoroughly nor considered lots of scenarios, but it seems to make Enumerable.Contains and Dictionary pretty happy.

alpha-mouse
  • 4,953
  • 24
  • 36
  • 3
    It's unfortunate that all these collections are written in a Java-esque way, requiring you to write a class that implements a specific interface. If only they allowed you to pass in a delegate to specify the comparison operator, you could pass `object.ReferenceEquals` directly. I guess it's because two methods are needed (comparison and hashcode). – Ben Voigt Feb 04 '11 at 18:24
  • As I understand it, though, the Java counterpart to `IEqualityComparer` doesn't have a `GetHashCode`, so it *could* be implemented as a delegate in Java if Java supported delegates. – Gabe Feb 04 '11 at 18:35
  • 1
    @Ben look at orip's answer: http://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer/1239337#1239337 – AK_ Feb 04 '11 at 18:49
  • @Hellfrost: great link. Too bad none of those were included with .NET. – Ben Voigt Feb 04 '11 at 18:52
  • One could define a `DelegateQualityComparer` that takes two delegates(one for equality, one for hashcode). And one that takes only one delegate mapping the compared type to the result of a delegate and comparing that one could be useful too as comparing a property is a common scenario. – CodesInChaos Feb 04 '11 at 18:57
  • too bad .net has references at all... – AK_ Feb 04 '11 at 19:03
  • @CodeInChaos: That's (a `DelegateEqualityComparer` implementation) pretty much the point of the link Hellfrost gave. – Ben Voigt Feb 04 '11 at 19:28
  • I mean that Reference Types are semantically strange and wrong, and conflict with value types, and its too bad that C# inherited this defect from Java. Reference types were supposed to be a remedy to the issues brought to C++ by pointers, but its completely like spilling the bath watter with the baby. – AK_ Feb 04 '11 at 19:32
  • 2
    See also: http://stackoverflow.com/questions/1890058/iequalitycomparert-that-uses-referenceequals – Eldritch Conundrum Sep 01 '13 at 15:28
  • See my update of the implementation to .Net4.0 [as an answer to a similar question here](http://stackoverflow.com/a/35520207/533837) - in short the comparer doesn't need to be generic anymore thanks to contravariance on the interface! (Which simplifies usage and saves a tiny bit of memory.) – AnorZaken Feb 20 '16 at 07:08

2 Answers2

17

As far as I know, the BCL doesn't expose any public types that implement IEqualityComparer<T> with reference-equality as of .NET 4.0 .

However, there do appear to be a bunch of internal types that do this, such as:

  • System.Dynamic.Utils.ReferenceEqualityComparer<T> (in System.Core)
  • System.Xaml.Schema.ReferenceEqualityComparer<T> (in System.Xaml).

I took a look at the implementations of these two types with reflector, and you'll be happy to know that both of them appear to be implemented in a way that is virtually identical to yours, except that they don't use lazy-initialization for the static instance (they create it in the static constructor for the type).

The only possible 'issue' I can think of with your implementation is that the lazy-initialization is not thread-safe, but since instances are 'cheap' and aren't holding onto any state, that shouldn't create any bugs or major performance problems. If you want to enforce the singleton-pattern though, you'll have to do it properly.

Ani
  • 111,048
  • 26
  • 262
  • 307
  • Thanks for mentioning my lazy-initialization failure. For this class it looks meaningful to initialize the instance in the static constructor. However, thanks to you, I'll not make the same mistake anywhere else. – alpha-mouse Feb 04 '11 at 18:32
  • @alpha-mouse: Cheers. I would say that I wouldn't feel obligated to make a class thread-safe just for the sake of it, unless I intend to *use* it in a manner that requires it. – Ani Feb 04 '11 at 18:50
  • There is also `System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer`. – Olivier Jacot-Descombes Jul 14 '17 at 14:01
  • `namespace System.Dynamic.Utils { internal sealed class ReferenceEqualityComparer { ...} }`: `internal` prohibits any direct use by us, and `sealed` also prohibits any indirect use. :-( – Tobias Knauss Jul 14 '19 at 09:19
2

I end up using this solution also as I could not find any workaround.

To fix the non thread safe implementation, you could easily use a static initiailizer.

public static ReferenceComparer<T> Instance => new ReferenceComparer<T>();

(sorry for the answer instead of a comment to up-voted thread, I have a new account with no comment rights, yet).

Samuel S.
  • 141
  • 4