Domanda

C'è un default IEqualityComparer<T> implementazione che utilizza ReferenceEquals?

EqualityComparer<T>.Default utilizza ObjectComparer, che utilizza object.Equals().Nel mio caso, gli oggetti che implementano già IEquatable<T>, che ho bisogno di ignorare e di confronto da parte dell'oggetto di riferimento.

È stato utile?

Soluzione

Solo nel caso in cui non vi è alcuna implementazione predefinita, questo è il mio:

Edit by 280Z28:Motivazioni per l'utilizzo di RuntimeHelpers.GetHashCode(object), che molti di voi probabilmente non avete visto prima.:) Questo metodo ha due effetti che rendono il corretto chiamata per questa implementazione:

  1. Restituisce 0 se l'oggetto è null.Dal ReferenceEquals funziona per i parametri null, così deve essere l'operatore di confronto implementazione del metodo GetHashCode().
  2. Si chiama Object.GetHashCode() non virtualmente. ReferenceEquals in particolare ignora eventuali sostituzioni di Equals, così l'implementazione del metodo GetHashCode() si deve utilizzare un metodo speciale che abbina l'effetto di ReferenceEquals, che è esattamente quello che RuntimeHelpers.GetHashCode è per.

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

Altri suggerimenti

Ho pensato che era tempo di aggiornare le precedenti risposte attuazione .Net4.0+, dove si semplifica, diventando non generico grazie per la controvarianza sul IEqualityComparer<in T> interfaccia:

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

Ora non solo deve esistere un esempio per tutti il vostro riferimento-uguaglianza di controllo, invece di uno per ogni tipo di T come era il caso prima.

Inoltre si salva digitando da non dover specificare T ogni volta che si desidera utilizzare questo!


Per chiarire per coloro che non hanno familiarità con i concetti di Covarianza e Controvarianza...

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

...funziona bene.Questo è non limitata, per esempio, HashSet<object> o simili (in .Net4.0).


Anche per chiunque si stia chiedendo perché x == y di riferimento è l'uguaglianza, perché la ==l'operatore è un metodo statico, il che significa che si è risolto a tempo di compilazione), e a tempo di compilazione) x e y sono di tipo object quindi qui si risolve l' ==operatore di object - quale è il reale riferimento parità di metodo.(Infatti il Object.ReferenceEquals(object, object) il metodo è semplicemente un redirect all'oggetto è uguale operatore.)

Ecco una semplice implementazione per 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);
}

Modifica (Non è necessario leggere questo a meno che non siete interessati nei commenti qui sotto)

@AnorZaken dedicato molti paragrafi per le tre lettere del modificatore new qui. Riassumiamo.

Il singolo metodo istanza Equals(object,object) definito implementa il metodo Equals delle due interfacce dichiarate per questo tipo, IEqualityComparer e suo generico IEqualityComparer<object> controparte. Le firme sono identici, così questo soddisfa definizione entrambe le interfacce.

Il metodo di istanza ReferenceEqualityComparer.Equals(object,object) nasconde la statica metodo object.Equals(object,object).

Senza new il compilatore mette in guardia su questo. Che cosa significa in concreto?

Ciò significa che se si desidera chiamare i metodi statici object.Equals, non si può chiamare su un istanza di ReferenceEqualityComparer. È questo un grosso problema?

No. In realtà è il comportamento desiderato. Ciò significa che se si desidera chiamare object.Equals(a,b) non è possibile farlo tramite codice come ReferenceEqualityComparer.Default.Equals(a,b). Tale codice è chiaramente richiede di riferimento uguaglianza - nessuno si sarebbe ragionevolmente aspettarsi di svolgere l'uguaglianza / valore predefinito. Perché non si basta codificare il object.Equals(a,b) più esplicita in ogni caso? Quindi l'uso di new fornisce un comportamento ragionevole e desiderabile, e permette la compilazione senza avvisi.

In quale altro modo si potrebbe eliminare l'avviso? Se si utilizza un #pragma warning disable 108 / #pragma warning restore 108 allora il risultato è lo stesso che utilizzare new, tranne che hai aggiunto un gruppo più rumore al codice. new sufficiente e spiega l'intento più chiaramente agli altri.

In alternativa è possibile utilizzare le implementazioni esplicite per i due metodi di interfaccia Equals, ma poi se è stato utilizzato ReferenceEqualityComparer.Default.Equals(a,b) non si avrebbe l'uguaglianza di riferimento a tutti.

In realtà, nasconde metodi statici con metodi di istanza è raramente un problema, perché i metodi statici sono dereferenziati da un indicatore di tipo, non un identificatore di istanza. Cioè, si utilizza Foo.StaticMethod() non new Foo().StaticMethod(). Chiamare i metodi statici dalle istanze è inutile nella migliore delle ipotesi e fuorvianti / inesatte nel peggiore dei casi.

Inoltre, per comparatori di uguaglianza, che raramente utilizzare i loro tipi concreti direttamente. Piuttosto, li usate con le API, come collezioni.

Così, mentre questo è stato un interessante e, a volte confondendo discussione, era piuttosto inutile.

Microsoft fornisce ObjectReferenceEqualityComparer in System.Data.Entity.Infrastructure. Basta usare ObjectReferenceEqualityComparer.Default come operatore di confronto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top