IEqualityComparer <t>使用ReferenceEquals </t>

时间:2009-12-11 18:40:47

标签: c# .net iequalitycomparer referenceequals

是否有使用IEqualityComparer<T>的默认ReferenceEquals实施?

EqualityComparer<T>.Default使用ObjectComparer,它使用object.Equals()。在我的例子中,对象已经实现了IEquatable<T>,我需要忽略它并按对象的引用进行比较。

5 个答案:

答案 0 :(得分:54)

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

编辑280Z28:使用RuntimeHelpers.GetHashCode(object)的基本原理,你们许多人可能以前从未见过。 :)此方法有两个效果,使其成为此实现的正确调用:

  1. 当对象为null时返回0。由于ReferenceEquals适用于null参数,因此比较器的GetHashCode()实现也是如此。
  2. 非虚拟地调用Object.GetHashCode()ReferenceEquals特别忽略了Equals的任何覆盖,因此GetHashCode()的实现应该使用与ReferenceEquals的效果相匹配的特殊方法,这正是RuntimeHelpers.GetHashCode的用途。
  3. [结束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
    }
    

答案 1 :(得分:14)

我认为现在是时候将之前的答案实施更新为.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);
    }
}

现在只需要为所有引用相等性检查存在一个实例,而不是像以前那样为每个类型T存在一个实例。

另外,每次要使用此项时,都不必指定T来保存输入!

澄清那些不熟悉Covariance and Contravariance ...

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

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

同样对于任何想知道为什么x == y是引用相等的人,这是因为==运算符是静态方法,这意味着它在编译时被解析,并且在编译时x和y属于object类型,因此它解析为==的{​​{1}}运算符 - 这是真正的引用相等方法。 (事实上​​,object方法只是重定向到对象equals运算符。)

答案 2 :(得分:7)

这是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)方法为此类型的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)的代码呢?因此new的使用提供了明智和理想的行为,并且允许编译而没有警告。

你怎么能抑制警告?如果您使用#pragma warning disable 108 / #pragma warning restore 108,则结果与使用new相同,除非您为代码添加了更多噪音。 new足以让其他人更明确地解释意图。

或者,您可以对两个接口Equals方法使用显式实现,但是如果您使用ReferenceEqualityComparer.Default.Equals(a,b),则根本不会有引用相等。

实际上,使用实例方法隐藏静态方法很少成为问题,因为静态方法是从类型说明符而不是实例说明符中取消引用的。也就是说,您使用Foo.StaticMethod()而不是new Foo().StaticMethod()。从实例调用静态方法最好是不必要的,最坏的情况是误导/不正确。

此外,对于相等比较器,您很少直接使用它们的具体类型。相反,您可以将它们用于诸如集合之类的API。

因此虽然这是一个有趣且有时令人困惑的讨论,但它却毫无结果。

答案 3 :(得分:5)

答案 4 :(得分:0)

Microsoft在ObjectReferenceEqualityComparer中提供System.Data.Entity.Infrastructure。 只需使用ObjectReferenceEqualityComparer.Default作为比较器即可。