Question

Y at-il une implémentation IEqualityComparer<T> par défaut qui utilise ReferenceEquals?

EqualityComparer<T>.Default utilise ObjectComparer, qui utilise object.Equals(). Dans mon cas, les objets implémentent déjà IEquatable<T>, que je dois ignorer et comparer par la référence de l'objet uniquement.

Était-ce utile?

La solution

Juste au cas où il n'y a pas implémentation par défaut, ceci est mon propre:

Modifier par 280Z28: Justification de l'utilisation RuntimeHelpers.GetHashCode(object) , dont beaucoup vous avez probablement jamais vu auparavant. :) Cette méthode a deux effets qui en font le correct appel à cette mise en œuvre:

  1. Il retourne 0 quand l'objet est nul. Depuis fonctionne ReferenceEquals pour les paramètres nuls, donc si la mise en œuvre du comparateur de GetHashCode ().
  2. Il appelle Object.GetHashCode() non virtuellement. ReferenceEquals ne tient pas compte spécifiquement des redéfinitions Equals, de sorte que la mise en œuvre de GetHashCode () devrait utiliser une méthode spéciale qui correspond à l'effet de ReferenceEquals, ce qui est exactement ce que RuntimeHelpers.GetHashCode est pour.

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

Autres conseils

Je pensais qu'il était temps de mettre à jour les réponses précédentes à la mise en œuvre .Net4.0 + où il simplifie en devenant grâce non générique contravariance sur l'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);
    }
}

a besoin là que pour exister une instance pour tous vos vérifiait au lieu d'un pour chaque T de type référence l'égalité comme ce fut le cas avant.

vous enregistrez également la saisie de ne pas avoir à spécifier T chaque fois que vous voulez utiliser!


Pour préciser pour ceux qui ne sont pas familiers avec les concepts de Covariance et Contravariance .. .

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

... fonctionnera très bien. Ceci est pas limité par exemple HashSet<object> ou similaire (en .Net4.0).


Aussi pour ceux qui se demandent pourquoi x == y est l'égalité de référence, il est parce que la ==operator est une méthode statique, ce qui signifie qu'il est résolu à la compilation et à la compilation x et y sont de type object donc ici, il décide de le ==operator de object - qui est le real méthode de l'égalité de référence. (En fait, le procédé de Object.ReferenceEquals(object, object) est simplement une redirection vers l'objet est égale à l'opérateur.)

Voici une implémentation simple pour 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);
}

EDIT (Vous ne devez pas lire, sauf si vous êtes intéressé par les commentaires ci-dessous)

@AnorZaken a consacré plusieurs paragraphes aux trois lettres du modificateur de new ici. Résumons.

La méthode Equals(object,object) instance définie unique implémente la méthode Equals des deux interfaces déclarées pour ce type, IEqualityComparer et son homologue IEqualityComparer<object> générique. Les signatures sont identiques, de sorte que cette définition satisfait aux deux interfaces.

La méthode d'instance ReferenceEqualityComparer.Equals(object,object) cache statique méthode object.Equals(object,object).

Sans new le compilateur met en garde à ce sujet. Qu'est-ce que cela signifie réellement?

Cela signifie que si vous voulez appeler les méthodes object.Equals statiques, vous ne pouvez pas l'appeler sur une par exemple de ReferenceEqualityComparer. Est-ce important?

Non. En fait, il est le comportement souhaité. Cela signifie que si vous voulez appeler object.Equals(a,b) vous ne pouvez pas le faire via un code tel que ReferenceEqualityComparer.Default.Equals(a,b). Ce code demande clairement référence l'égalité - personne ne peut raisonnablement s'attendre à réaliser l'égalité par défaut / valeur. Pourquoi ne pas le code juste object.Equals(a,b) plus explicite de toute façon? Ainsi, l'utilisation de new fournit un comportement raisonnable et souhaitable, et permet la compilation sans avertissement.

Sinon, comment pourriez-vous supprimer l'avertissement? Si vous utilisez un #pragma warning disable 108 / #pragma warning restore 108 alors le résultat est le même que l'utilisation new, sauf que vous avez ajouté un tas plus de bruit à votre code. new suffit et explique l'intention plus clairement aux autres.

Sinon, vous pouvez utiliser des implémentations explicites pour les deux méthodes de Equals d'interface, mais si vous avez utilisé ReferenceEqualityComparer.Default.Equals(a,b) vous pas l'égalité de référence du tout.

En réalité, cachant des méthodes statiques avec des méthodes d'instance est rarement un problème parce que les méthodes statiques sont déréférencé d'un spécificateur de type, pas un prescripteur d'instance. Autrement dit, vous utilisez Foo.StaticMethod() pas new Foo().StaticMethod(). Appel de méthodes statiques de cas est inutile au mieux et trompeur / incorrect au pire.

En outre, pour l'égalité des comparateurs, vous utilisez rarement leurs types de béton directement. Au contraire, vous les utiliser avec les API telles que les collections.

Ainsi, alors que cela était intéressant et parfois déroutant discussion, il était plutôt inutile.

Microsoft fournit ObjectReferenceEqualityComparer dans System.Data.Entity.Infrastructure. Il suffit d'utiliser ObjectReferenceEqualityComparer.Default comme comparateur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top