Reputation: 2902
I have written a generic equality comparer that should always compare by reference, no matter how the GetHashCode
and Equals
methods of the parameter type look like:
public class ReferenceEqualityComparer<T> : IEqualityComparer<T>
{
public static ReferenceEqualityComparer<T> Inst = new ReferenceEqualityComparer<T>();
private ReferenceEqualityComparer() { }
public bool Equals(T x, T y) { return ReferenceEquals(x, y); }
public int GetHashCode(T obj) { return RuntimeHelpers.GetHashCode(obj); }
}
I threw in a ValueTuple
(in this case a pair of object
s) like so:
var objectPairComparer = ReferenceEqualityComparer<(object,object)>.Inst;
But this comparer does not behave as expected, so I guess I am doing something wrong. To see what's wrong, first consider the following situation:
object a = new object();
object b = new object();
object c = a;
object d = b;
HashSet<(object, object)> set = new HashSet<(object, object)>();
Console.WriteLine("set.Add((a, b)) = " + set.Add((a, b))); // returns true
Console.WriteLine("set.Contains((c, d)) = " + set.Contains((c, d))); // returns true
Console.WriteLine("set.Add((c, d)) = " + set.Add((c, d))); // returns false
Since no comparer is given as input to the HashSet
, the default comparer will be used. That means, both Item1
and Item2
will use reference equality and default hash code (the address or something). The output is what I expect.
But if I instead use the comparer
HashSet<(object,object)> set = new HashSet<(object,object)>(objectPairComparer);
then the output changes:
Console.WriteLine("set.Add((a, b)) = " + set.Add((a, b))); // returns true like before
Console.WriteLine("set.Contains((c, d)) = " + set.Contains((c, d))); // returns FALSE
Console.WriteLine("set.Add((c, d)) = " + set.Add((c, d))); // returns TRUE
But they should behave the same way! Why don't they? Isn't ReferenceEquals
the same as object.Equals
, and isn't ReferenceEquals
used on two (object,object)
the same as using ReferenceEquals
both on the Item1
s and Item2
s and &&
ing the results? And GetHashCode
analogous?
Upvotes: 0
Views: 580
Reputation: 203820
Isn't ReferenceEquals the same as object.Equals
No, it is not. object.Equals
will use virtual dispatch on the first operand, finding it's implementation of Equals
for the actual runtime type of the object, and use whatever that type's definition says to do. In the case of ValueTuple
, it'll compare the actual values of the two tuples. ReferenceEquals
just compares the references and tells you if they're equal. In this particular case you have two different references, even though the value that each reference references are the same.
isn't ReferenceEquals used on two (object,object) the same as using ReferenceEquals both on the Item1s and Item2s and &&ing the results?
No, it's not. It's only going to tell you if the two objects you pass in are both the same reference to the same object. They won't inspect the actual values of those objects. In this case, you have two different references, so they're not equal.
And GetHashCode analogous?
It is analogous, insofar as the first version is using the ValueTuple
implementation that computes the hash based on the value of the items within the tuple, while the second computes the hash entirely based on the reference to the object itself, so when you have two different references to two different objects, but where those objects but where those two objects have equivalent values internally, the first considers them equal, the second considers them unequal.
Upvotes: 1