通用引用相等比较器不适用于 ValueTuple

generic reference equality comparer does not work for ValueTuple

我写了一个通用的相等比较器,它应该总是通过引用进行比较,无论参数类型的 GetHashCodeEquals 方法看起来如何:

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); }
}

我像这样输入 ValueTuple(在本例中是一对 object):

var objectPairComparer = ReferenceEqualityComparer<(object,object)>.Inst;

但是这个比较器没有按预期运行,所以我想我做错了什么。要查看错误,首先考虑以下情况:

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

由于没有比较器作为 HashSet 的输入,将使用默认比较器。这意味着,Item1Item2 都将使用引用相等性和默认哈希码(地址或其他内容)。输出是我所期望的。

但是如果我改为使用比较器

HashSet<(object,object)> set = new HashSet<(object,object)>(objectPairComparer);

然后输出改变:

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

但他们应该以同样的方式行事!他们为什么不呢? ReferenceEquals 是否与 object.Equals 相同,并且 ReferenceEquals 用于两个 (object,object) 是否与在 [=20= 上同时使用 ReferenceEquals 相同]s 和 Item2s 和 &&ing 结果?和GetHashCode类似吗?

Isn't ReferenceEquals the same as object.Equals

不,不是。 object.Equals 将在第一个操作数上使用虚拟分派,发现它是 Equals 对象的实际运行时类型的实现,并使用该类型的定义所说的任何内容。在 ValueTuple 的情况下,它将比较两个元组的实际值。 ReferenceEquals 只是比较引用并告诉您它们是否相等。在这种特殊情况下,您有两个不同的引用,即使每个引用引用的值相同。

isn't ReferenceEquals used on two (object,object) the same as using ReferenceEquals both on the Item1s and Item2s and &&ing the results?

不,不是。 会告诉您传入的两个对象是否都是对同一对象的相同引用。他们不会检查这些对象的实际值。在这种情况下,您有两个不同的引用,因此它们不相等。

And GetHashCode analogous?

这是类似的,因为第一个版本使用 ValueTuple 实现,该实现根据元组中的项目的值计算散列,而第二个版本完全基于对的引用来计算散列对象本身,所以当你对两个不同的对象有两个不同的引用时,但是那些对象但是这两个对象在内部具有相等的值时,第一个认为它们相等,第二个认为它们不相等。