Pergunta

Existe uma implementação IEqualityComparer<T> padrão que usa ReferenceEquals?

EqualityComparer<T>.Default usa ObjectComparer, que usa object.Equals(). No meu caso, os objetos já implementar IEquatable<T>, o que eu preciso ignorar e comparar por única referência do objeto.

Foi útil?

Solução

Apenas no caso não há nenhuma implementação padrão, essa é a minha própria:

Editar por 280Z28: Justificativa para o uso RuntimeHelpers.GetHashCode(object) , que muitos dos você provavelmente não tenha visto antes. :) Este método tem dois efeitos que tornam o correta chamada para esta implementação:

  1. Ele retorna 0 quando o objeto é nulo. Desde ReferenceEquals trabalha para parâmetros nulos, assim se a implementação do comparador de GetHashCode ().
  2. Object.GetHashCode() não virtualmente. ReferenceEquals especificamente ignora quaisquer substituições de Equals, de modo a implementação de GetHashCode () deve usar um método especial que combina o efeito de ReferenceEquals, que é exatamente o que RuntimeHelpers.GetHashCode é para.

[end 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
}

Outras dicas

Eu pensei que era hora de atualizar a implementação respostas anterior para .Net4.0 +, onde ele simplifica, tornando-se graças não-genéricos para contravariance na interface 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);
    }
}

Agora só precisa de existir uma instância para toda a verificação em vez de um para cada tipo T como foi o caso antes de sua referência-igualdade.

Além disso, você poupar digitação por não ter que especificar T cada vez que você quiser usar este!


Para esclarecer para aqueles que não estão familiarizados com os conceitos de Covariância e Contravariance .. .

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

... vai funcionar muito bem. Esta é não se limitando a, por exemplo, HashSet<object> ou similar (em .Net4.0).


Também para qualquer um se perguntando por que x == y é igualdade de referência, é porque o ==operator é um método estático, o que significa que é resolvido em tempo de compilação e em tempo de compilação x e y são do tipo object então aqui resolve o ==operator de object - que é a real método de igualdade de referência. (Na verdade, o método Object.ReferenceEquals(object, object) é simplesmente um redirecionamento para o objeto é igual operador.)

Aqui está uma implementação simples para 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);
}

Editar (Você não tem que ler este a menos que você está interessado nos comentários abaixo)

@AnorZaken dedicou muitos parágrafos para as três letras do new modificador aqui. Vamos resumir.

Os únicos definidos exemplo Equals(object,object) método implementa o método Equals das duas interfaces declaradas para este tipo, IEqualityComparer e sua IEqualityComparer<object> homólogo genérico. As assinaturas são idênticas, pelo que esta satisfaz definição ambas as interfaces.

O método da ocorrência peles ReferenceEqualityComparer.Equals(object,object) o estática método object.Equals(object,object).

Sem new o compilador avisa sobre isso. O que isso realmente significa?

Isso significa que se você quiser chamar os métodos object.Equals estático, você não pode chamá-lo em um exemplo de ReferenceEqualityComparer. É este um grande negócio?

No. Na verdade, é o comportamento desejado. Isso significa que se você quiser chamar object.Equals(a,b) você não pode fazê-lo através de código como ReferenceEqualityComparer.Default.Equals(a,b). Esse código é claramente solicitando referência igualdade - ninguém seria razoável esperar que ele para executar igualdade padrão / valor. Por que você não apenas codificar o object.Equals(a,b) mais explícito de qualquer maneira? Assim, o uso de new fornece comportamento sensato e desejável, e permite a compilação com nenhum aviso.

Como mais você poderia suprimir o aviso? Se você usar um #pragma warning disable 108 / #pragma warning restore 108 então o resultado é o mesmo que usar new, exceto que você adicionou mais um monte de ruído ao seu código. sufixos new e explica a intenção mais claramente para os outros.

Como alternativa, você poderia usar implementações explícitas para os dois métodos de interface Equals, mas, em seguida, se você ReferenceEqualityComparer.Default.Equals(a,b) usado você não teria igualdade de referência em tudo.

Na realidade, os métodos de ocultação estáticas com métodos de instância raramente é um problema porque os métodos estáticos são dereferenced de um especificador tipo, não um especificador instância. Ou seja, você usa Foo.StaticMethod() não new Foo().StaticMethod(). Chamar métodos estáticos de instâncias é desnecessário no melhor e enganosa / incorreto na pior das hipóteses.

Além disso, para comparadores de igualdade, você raramente usam seus tipos de concreto diretamente. Em vez disso, você usá-los com APIs, tais como coleções.

Assim, embora este foi um interessante e às vezes confundindo discussão, foi bastante infrutíferas.

Microsoft fornecer ObjectReferenceEqualityComparer em System.Data.Entity.Infrastructure. Basta usar ObjectReferenceEqualityComparer.Default como comparador.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top