IEqualityComparer , die verwendet Reference
-
19-09-2019 - |
Frage
Gibt es eine Standard-Implementierung, die IEqualityComparer<T>
ReferenceEquals
verwendet?
EqualityComparer<T>.Default
verwendet ObjectComparer, die object.Equals()
verwendet. In meinem Fall implementieren die Objekte bereits IEquatable<T>
, die ich nur durch Objekt Bezug zu ignorieren und vergleichen müssen.
Lösung
Nur für den Fall gibt es keine Default-Implementierung, das ist meine eigene:
Bearbeiten von 280Z28: Rationale für die Verwendung von RuntimeHelpers.GetHashCode(object)
, die viele Sie haben wahrscheinlich noch nie zuvor gesehen. :) Diese Methode hat zwei Effekte, die es der richtig Aufruf für diese Implementierung machen:
- Es gibt 0 zurück, wenn das Objekt null ist. Da
ReferenceEquals
für null Parameter arbeitet, so sollte die Umsetzung des Vergleichs von GetHashCode (). - Sie ruft
Object.GetHashCode()
nicht praktisch.ReferenceEquals
ignoriert ausdrücklich jegliche Überschreibungen vonEquals
, so die Implementierung von GetHashCode () soll eine spezielle Methode, die die Wirkung von Referenceübereinstimmt, das ist genau das, was RuntimeHelpers.GetHashCode für ist.
[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
}
Andere Tipps
Ich dachte, es war Zeit, die bisherigen Antworten Implementierung .Net4.0 zu aktualisieren + wo es vereinfacht, indem er nicht generischen Dank auf der IEqualityComparer<in T>
Schnittstelle zum Kontra:
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);
}
}
Jetzt braucht es nur eine Instanz für alle Ihre Referenz-Gleichheit Überprüfung statt einer für jeden Typ T
zu existieren, wie der Fall war vor.
Sie auch die Eingabe sparen, indem nicht jedes Mal, wenn Sie möchten, geben Sie T
mit dieser verwenden!
für diejenigen Um zu klären, die nicht vertraut sind mit den Konzepten der Kovarianz und Kontra .. .
class MyClass
{
ISet<MyClass> setOfMyClass = new HashSet<MyClass>(ReferenceEqualityComparer.Default);
}
... wird gut funktionieren. Dies ist nicht beschränkt auf z.B. HashSet<object>
oder ähnlich (in .Net4.0).
Auch für jemanden fragen, warum x == y
Referenz Gleichheit wird, ist es, weil der ==
operator eine statische Methode ist, das heißt, es zur Compile-Zeit aufgelöst wird, und zum Zeitpunkt der Kompilierung x und y ist vom Typ object
so ist es hier beschließt, der ==
operator von object
- das ist die real Referenz Gleichheit Methode. (In der Tat das Object.ReferenceEquals(object, object)
Methode ist einfach eine Umleitung an den Objektoperator entspricht.)
Hier ist eine einfache Implementierung für 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);
}
Bearbeiten (Sie müssen dies nicht lesen, wenn Sie in den Kommentaren unten interessiert sind)
@AnorZaken gewidmet viele Absätze auf die drei Buchstaben des new
Modifikator hier. Fassen wir zusammen.
Die einzige definierte Instanz Equals(object,object)
Methode implementiert die Equals
Verfahren der beiden erklärt Schnittstellen für diese Art, IEqualityComparer
und seine generische Gegenstück IEqualityComparer<object>
. Die Signaturen sind identisch, so dass diese Definition erfüllt beide Schnittstellen.
Die Instanzmethode ReferenceEqualityComparer.Equals(object,object)
versteckt die statisch object.Equals(object,object)
Methode.
Ohne new
warnt der Compiler darüber. Was bedeutet das eigentlich bedeuten?
Es bedeutet, dass, wenn Sie die statische object.Equals
Methoden aufrufen möchten, können Sie es nicht nennen kann auf eine Instanz von ReferenceEqualityComparer
. Ist das ein Problem?
Nein. In der Tat ist es das Verhalten gewünscht. Es bedeutet, dass, wenn Sie object.Equals(a,b)
anrufen möchten Sie es über den Code nicht tun können, wie ReferenceEqualityComparer.Default.Equals(a,b)
. Dieser Code anfordert eindeutig Hinweis Gleichheit - niemand würde vernünftigerweise erwarten, dass es default / Wertgleichheit durchzuführen. Warum sollen nicht codieren Sie nur den expliziten object.Equals(a,b)
überhaupt? So ist die Verwendung von new
bietet sinnvolle und wünschenswertes Verhalten und ermöglicht Kompilierung ohne Warnungen.
Wie sonst könnte man die Warnung unterdrücken? Wenn Sie eine #pragma warning disable 108
/ #pragma warning restore 108
verwenden dann ist das Ergebnis das gleiche wie mit new
, außer dass Sie ein paar mehr Lärm zu Ihrem Code hinzugefügt haben. new
genügt und erklärt die Absicht deutlicher zu anderen.
Alternativ können Sie explizite Implementierungen für die beiden Schnittstelle Equals
Methoden verwenden, aber dann, wenn Sie ReferenceEqualityComparer.Default.Equals(a,b)
verwendet würden Sie nicht Verweis Gleichheit überhaupt haben.
In Wirklichkeit statische Methoden mit Instanzmethoden versteckt ist selten ein Problem, da statische Methoden von einem Typspezifizierer dereferenziert werden, keine Instanz Spezifizierer. Das heißt, Sie verwenden Foo.StaticMethod()
nicht new Foo().StaticMethod()
. Der Aufruf statische Methoden von Instanzen ist nicht notwendig, am besten und irreführend / falsch im schlimmsten Fall.
Ferner für die Gleichstellung comparers, Sie selten ihre konkreten Typen direkt verwenden. Vielmehr nutzen Sie sie mit APIs wie Sammlungen.
Während also das war eine interessante und manchmal verwirrend Diskussion, es war eher fruchtlos.
Microsoft bietet ObjectReferenceEqualityComparer
in System.Data.Entity.Infrastructure
.
Verwenden Sie einfach ObjectReferenceEqualityComparer.Default
als Vergleich.