.NET中是否有任何类型的“ReferenceComparer”?

19

在BCL中,有几个地方可以使用IEqualityComparer,例如Enumerable.ContainsDictionary Constructor。如果我不满意default的比较器,我可以提供自己的。

有时候我想知道集合中是否包含我所引用的那个对象,而不是其他任何意义上“相等”的对象。
问题是:BCL中是否存在只依赖于ReferenceEquals方法的标准相等比较器?

我自己编写的比较器如下:

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

我没有全面测试过,也没有考虑很多场景,但似乎可以让Enumerable.ContainsDictionary非常满意。


3
很不幸,所有这些集合都是以类似Java的方式编写的,需要你编写一个实现特定接口的类。如果它们允许你传递一个委托来指定比较运算符,那么你就可以直接传递object.ReferenceEquals。我想这是因为需要两个方法(比较和哈希码)。 - Ben Voigt
1
@Ben 看看 orip 的回答:https://dev59.com/M3VD5IYBdhLWcg3wE3Xz#1239337 - AK_
我的意思是引用类型在语义上很奇怪、错误,并与值类型发生冲突,很遗憾C#从Java继承了这个缺陷。引用类型本应是对指针在C++中带来问题的治疗方法,但却完全像是连同婴儿一起泼掉了洗澡水。 - AK_
2
参见:https://dev59.com/KnI-5IYBdhLWcg3wYXL8 - Eldritch Conundrum
请看我对.Net4.0实现的更新,在这里回答了类似问题——简而言之,由于接口上逆变性的存在,比较器不再需要是泛型!(这简化了使用并节省了一点内存。) - AnorZaken
显示剩余5条评论
2个回答

18
据我所知,截至 .NET 4.0,BCL没有公开实现了IEqualityComparer<T>接口的引用相等的任何公共类型。
但是,似乎有大量内部类型可以实现这一点,例如:
  • System.Dynamic.Utils.ReferenceEqualityComparer<T>(位于 System.Core 中)
  • System.Xaml.Schema.ReferenceEqualityComparer<T>(位于 System.Xaml 中)
我使用反射器查看了这两种类型的实现方式,你会很高兴地知道它们的实现方式几乎与你的完全相同,只是它们不使用静态实例的延迟初始化(它们在类型的静态构造函数中创建它)。
我能想到的唯一可能的“问题”是,你的实现方法不是线程安全的,但由于实例很“便宜”,并且不保留任何状态,因此不应该产生任何错误或主要性能问题。如果你想要“强制实施”单例模式,那么你必须正确地实现它。

1
感谢您提及我的延迟初始化失败。对于这个类,将实例初始化在静态构造函数中似乎很有意义。但是,多亏了您,我将不会在其他任何地方犯同样的错误。 - alpha-mouse
1
@alpha-mouse:干杯。我认为,除非我打算以需要线程安全的方式使用它,否则我不会感到有义务仅仅为了这个目的而使一个类线程安全。 - Ani
还有System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer - Olivier Jacot-Descombes
命名空间 System.Dynamic.Utils 中,有一个内部密封类 ReferenceEqualityComparer { ... }。internal 修饰符禁止我们直接使用该类,而 sealed 修饰符则禁止任何间接使用。 :-( - Tobias Knauss

3

由于找不到其他解决方法,我最终采用了这个方案。

为了修复非线程安全的实现,您可以轻松地使用静态初始化程序。

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

(对于已经被点赞的帖子,我很抱歉不能发表评论,因为我是一个新账户,还没有评论权限。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接