Есть ли какой-нибудь ReferenceComparer в .NET?

В BCL есть несколько мест, где можно использовать IEqualityComparer. Например, Enumerable.Contains или Конструктор словарей. Я могу предоставить свой компаратор, если меня не устраивает вариант по умолчанию.

Иногда мне нужно знать, содержит ли коллекция тот самый объект, на который я ссылаюсь. Не тот, который считается «равным» в любом другом смысле.
Вопрос в следующем: существует ли в 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.Contains и Dictionary очень счастливы.


person alpha-mouse    schedule 04.02.2011    source источник
comment
К сожалению, все эти коллекции написаны в стиле Java, что требует написания класса, реализующего определенный интерфейс. Если бы только они позволили вам передать делегата для указания оператора сравнения, вы могли бы передать object.ReferenceEquals напрямую. Думаю, это потому, что нужны два метода (сравнение и хэш-код).   -  person Ben Voigt    schedule 04.02.2011
comment
Однако, насколько я понимаю, Java-аналог IEqualityComparer не имеет GetHashCode, поэтому он может быть реализован как делегат в Java, если Java поддерживает делегаты.   -  person Gabe    schedule 04.02.2011
comment
@Ben посмотрите ответ orip: stackoverflow.com/questions/98033/   -  person AK_    schedule 04.02.2011
comment
@Hellfrost: отличная ссылка. Жаль, что ни один из них не был включен в .NET.   -  person Ben Voigt    schedule 04.02.2011
comment
Можно определить DelegateQualityComparer<T>, который принимает два делегата (один для равенства, один для хэш-кода). И тот, который использует только один делегат, сопоставляющий сравниваемый тип с результатом делегата и сравнивающий этот тип, тоже может быть полезен, поскольку сравнение свойства - распространенный сценарий.   -  person CodesInChaos    schedule 04.02.2011
comment
жаль .net вообще есть ссылки ...   -  person AK_    schedule 04.02.2011
comment
@CodeInChaos: Это (реализация DelegateEqualityComparer) в значительной степени суть ссылки, которую дал Hellfrost.   -  person Ben Voigt    schedule 04.02.2011
comment
Я имею в виду, что ссылочные типы семантически странны и неправильны и конфликтуют с типами значений, и очень жаль, что C # унаследовал этот дефект от Java. Предполагалось, что ссылочные типы будут средством от проблем, вызванных указателями в C ++, но это полностью похоже на проливание воды в ванне на ребенка.   -  person AK_    schedule 04.02.2011
comment
См. Также: stackoverflow.com/questions/1890058/   -  person Eldritch Conundrum    schedule 01.09.2013
comment
См. Мое обновление реализации до .Net4.0 как ответ на аналогичный вопрос здесь - короче говоря, компаратор не не нужно больше быть универсальным из-за контравариантности интерфейса! (Это упрощает использование и экономит немного памяти.)   -  person AnorZaken    schedule 20.02.2016


Ответы (2)


Насколько мне известно, BCL не предоставляет никаких общедоступных типов, реализующих IEqualityComparer<T> с равенством ссылок, начиная с .NET 4.0.

Однако, похоже, существует множество внутренних типов, которые делают это, например:

  • System.Dynamic.Utils.ReferenceEqualityComparer<T> (в System.Core)
  • System.Xaml.Schema.ReferenceEqualityComparer<T> (в System.Xaml).

Я рассмотрел реализации этих двух типов с отражателем, и вы будете счастливы узнать, что оба они, похоже, реализованы способом, который практически идентичен вашему, за исключением того, что они не используйте ленивую инициализацию для статического экземпляра (они создают его в статическом конструкторе для типа).

Единственная возможная «проблема», о которой я могу думать с вашей реализацией, заключается в том, что ленивая инициализация не является потокобезопасной, но поскольку экземпляры «дешевы» и не удерживаются ни в каком состоянии, это не должно создавать никаких ошибок или серьезных ошибок. проблемы с производительностью. Если вы хотите принудительно применить шаблон singleton, вам нужно будет сделать это правильно.

person Ani    schedule 04.02.2011
comment
Спасибо, что упомянули о моей неудачной ленивой инициализации. Для этого класса имеет смысл инициализировать экземпляр в статическом конструкторе. Однако благодаря вам я больше нигде не совершу такой ошибки. - person alpha-mouse; 04.02.2011
comment
@ alpha-mouse: Ура. Я бы сказал, что не буду чувствовать себя обязанным делать класс поточно-ориентированным только ради этого, если только я не намерен использовать его так, как этого требует. - person Ani; 04.02.2011
comment
Также есть System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer. - person Olivier Jacot-Descombes; 14.07.2017
comment
namespace System.Dynamic.Utils { internal sealed class ReferenceEqualityComparer { ...} }: internal запрещает любое прямое использование нами, а sealed также запрещает любое косвенное использование. :-( - person Tobias Knauss; 14.07.2019

Я тоже использую это решение, так как не смог найти никакого обходного пути.

Чтобы исправить небезопасную реализацию потоков, вы можете легко использовать статический инициализатор.

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

(извините за ответ вместо комментария к ветке с одобренным голосованием, у меня пока нет новой учетной записи без прав на комментарии).

person Samuel S.    schedule 16.12.2015