Pregunta

¿Existe un valor predeterminado? IEqualityComparer<T> implementación que utiliza ReferenceEquals?

EqualityComparer<T>.Default utiliza ObjectComparer, que utiliza object.Equals().En mi caso, los objetos ya implementan IEquatable<T>, que necesito ignorar y comparar solo por referencia del objeto.

¿Fue útil?

Solución

En caso de que no haya una implementación predeterminada, esta es la mía:

Editado por 280Z28:Justificación del uso RuntimeHelpers.GetHashCode(object), que muchos de ustedes probablemente no hayan visto antes.:) Este método tiene dos efectos que lo convierten en el correcto Convocatoria para esta implementación:

  1. Devuelve 0 cuando el objeto es nulo.Desde ReferenceEquals funciona para parámetros nulos, al igual que la implementación del comparador de GetHashCode().
  2. llama Object.GetHashCode() no virtualmente. ReferenceEquals ignora específicamente cualquier anulación de Equals, por lo que la implementación de GetHashCode() debería utilizar un método especial que coincida con el efecto de ReferenceEquals, que es exactamente para lo que sirve RuntimeHelpers.GetHashCode.

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

Otros consejos

pensé que era hora de actualizar la aplicación previa respuestas a .Net4.0 + donde se simplifica gracias al convertirse no genérica a contravarianza en la interfaz 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);
    }
}

Ahora no sólo tiene que existir una instancia para toda su referencia igualdad comprobando en lugar de uno para cada tipo T como era el caso antes.

También se ahorra tener que escribir al no tener que especificar T cada vez que desee utilizar esto!


Para aclarar para aquellos que no están familiarizados con los conceptos de Covarianza y contravarianza .. .

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

... funcionará bien. Esto es no limitado a, por ejemplo, HashSet<object> o similar (en .Net4.0).


También para cualquiera que se pregunte por qué x == y es la igualdad de referencia, es porque el ==operator es un método estático, lo que significa que se resuelve en tiempo de compilación y en tiempo de compilación x e y son de tipo object lo que aquí se resuelve a la ==operator de object - que es el verdadero método la igualdad de referencia. (De hecho, el método Object.ReferenceEquals(object, object) es simplemente una redirección al objeto es igual operador.)

Esto es una implementación simple 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 (Usted no tiene que leer este a menos que esté interesado en los comentarios a continuación)

@AnorZaken dedicado muchos párrafos a las tres letras del modificador new aquí. Vamos a resumir.

El método de instancia Equals(object,object) definido solo implementa el método Equals de las dos interfaces declaradas para este tipo, IEqualityComparer y su genérico IEqualityComparer<object> contraparte. Las firmas son idénticas, por lo que esta definición satisface ambas interfaces.

oculta la ReferenceEqualityComparer.Equals(object,object) método de instancia la estática método object.Equals(object,object).

Sin new el compilador advierte acerca de esto. Lo que significa esto realmente?

Esto significa que si desea llamar a los métodos object.Equals estáticas, no se puede llamar en un ejemplo de ReferenceEqualityComparer. Es este un problema?

No. De hecho, es el comportamiento deseado. Esto significa que si usted desea llamar object.Equals(a,b) no puede hacerlo a través de un código como ReferenceEqualityComparer.Default.Equals(a,b). Ese código está solicitando claramente referencia la igualdad - nadie esperaría razonablemente que éste realice la igualdad / valor predeterminado. ¿Por qué no acaba de codificar el object.Equals(a,b) más explícito de todos modos? Por lo que el uso de new ofrece un comportamiento razonable y deseable, y permite la compilación sin advertencias.

¿Cómo más se puede suprimir la advertencia? Si utiliza un #pragma warning disable 108 / #pragma warning restore 108 entonces el resultado es el mismo que usar new, salvo que haya añadido un montón más ruido a su código. new basta y explica la intención con mayor claridad a los demás.

Como alternativa puede usar implementaciones explícitas para los dos métodos de interfaz Equals, pero luego si se ha utilizado ReferenceEqualityComparer.Default.Equals(a,b) que no tendría la igualdad de referencia en absoluto.

En realidad, ocultando los métodos estáticos con los métodos de instancia no suele ser un problema, ya que los métodos estáticos se eliminan las referencias de un especificador de tipo, no es un especificador de ejemplo. Es decir, se utiliza Foo.StaticMethod() no new Foo().StaticMethod(). Llamar a métodos estáticos de los casos no es necesaria en el mejor y engañosa / incorrecta en el peor.

Además, para los comparadores de igualdad, que rara vez utiliza sus tipos concretos directamente. Por el contrario, los utiliza con las API, tales como colecciones.

Así, pese a que esto era una interesante y en ocasiones confusas discusión, era bastante inútil.

Microsoft proporciona ObjectReferenceEqualityComparer en System.Data.Entity.Infrastructure. Sólo tiene que utilizar ObjectReferenceEqualityComparer.Default como comparador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top