IEqualityComparer <T>使用ReferenceEquals

有没有使用ReferenceEquals的默认IEqualityComparer<T>实现?

EqualityComparer<T>.Default使用ObjectComparer,它使用object.Equals() 。 在我的情况下,对象已经实现了IEquatable<T> ,我只需要忽略和比较对象的引用。

以防万一没有默认的实现,这是我自己的:

编辑280Z28:使用RuntimeHelpers.GetHashCode(object)理由,你可能以前没有见过。 :)这个方法有两个效果,使它成为这个实现的正确调用:

  1. 当对象为空时,它返回0。 由于ReferenceEquals适用于null参数,所以比较器的GetHashCode()实现也是如此。
  2. 它非虚拟地调用Object.GetHashCode()ReferenceEquals特别忽略了Equals任何覆盖,所以GetHashCode()的实现应该使用特殊的方法来匹配ReferenceEquals的效果,这正是RuntimeHelpers.GetHashCode的作用。

[结束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 } 

我认为现在是时候把以前的答案更新到.Net4.0 +,通过在IEqualityComparer<in T>界面上的逆变而变得非泛化,

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

现在只需要为所有引用相等检查存在一个实例,而不是每个typesT都有一个实例,就像以前一样。

你也可以节省打字时间,而不必在每次使用时都指定T


澄清那些不熟悉协方差和反variables概念的人

 class MyClass { ISet<MyClass> setOfMyClass = new HashSet<MyClass>(ReferenceEqualityComparer.Default); } 

…会工作得很好。 这不限于例如HashSet<object>或类似的(在.Net4.0中)。


对于任何想知道为什么x == y是引用相等的人,这是因为==运算符是一个静态方法,这意味着它在编译时被parsing,而在编译时x和y是typesobject所以在这里parsing为object==运算符 – 这是真正的引用等式方法。 (实际上, Object.ReferenceEquals(object, object)方法只是一个redirect到object对象的操作符。)

这是C#6的简单实现。

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

编辑 (你不必读这个,除非你有兴趣在下面的评论)

@AnorZaken在这里为new修饰语的三个字母写了许多段落。 我们总结一下。

单个定义的实例Equals(object,object)方法实现了这个types的两个声明接口的Equals方法, IEqualityComparer和它的通用对应的IEqualityComparer<object> 。 签名是相同的,所以这个定义满足两个接口。

实例方法ReferenceEqualityComparer.Equals(object,object)隐藏静态 object.Equals(object,object)方法。

没有new的编译器警告这一点。 这实际上是什么意思?

这意味着如果你想调用静态的object.Equals方法,你不能在ReferenceEqualityComparer实例上调用它。 这是一个大问题吗?

不。事实上,这是期望的行为。 这意味着如果你想调用object.Equals(a,b)你不能通过ReferenceEqualityComparer.Default.Equals(a,b)等代码来完成。 该代码明确要求参考平等 – 没有人会合理地期望它执行默认/值的平等。 为什么不直接编写更明确的object.Equals(a,b)呢? object.Equals(a,b)呢? 所以new的使用提供了明智的和理想的行为,并且允许编译而不用警告。

你还可以怎样压制这个警告? 如果您使用#pragma warning disable 108 / #pragma warning restore 108则结果与使用new的结果相同,只是您已向代码中添加了更多噪声。 new足够,并更清楚地解释其他人的意图。

或者,你可以使用显式实现的两个接口Equals方法,但是如果你使用了ReferenceEqualityComparer.Default.Equals(a,b)你根本就没有引用的相等性。

实际上,使用实例方法隐藏静态方法几乎不是问题,因为静态方法是从types说明符中取消引用的,而不是实例说明符。 也就是说,你使用Foo.StaticMethod()而不是new Foo().StaticMethod() 。 从实例中调用静态方法是不必要的,最糟糕的是误导/不正确。

而且,对于相等比较器,您很less直接使用它们的具体types。 而是将它们与API(如集合)一起使用。

所以虽然这是一个有趣的,有时令人困惑的讨论,但它是没有结果的。