IEqualityComparer, который использует ReferenceEquals
есть ли значение по умолчанию IEqualityComparer<T>
реализация, которая использует ReferenceEquals
?
EqualityComparer<T>.Default
использует ObjectComparer, который использует object.Equals()
. В моем случае, объекты уже реализованы IEquatable<T>
, который мне нужно игнорировать и сравнивать только по ссылке объекта.
3 ответа:
на всякий случай нет реализации по умолчанию, это мой собственный:
Edit by 280Z28: обоснование использования
RuntimeHelpers.GetHashCode(object)
, которое многие из вас, вероятно, не видели раньше. :) Этот метод имеет два эффекта, которые делают его правильно вызов для этой реализации:
- он возвращает 0, когда объект равен нулю. Так как
ReferenceEquals
работает для нулевых параметров, так что следует реализация компаратора GetHashCode().- It звонит
Object.GetHashCode()
не-практически.ReferenceEquals
специально игнорирует любые переопределенияEquals
, поэтому реализация GetHashCode () должна использовать специальный метод, который соответствует эффекту ReferenceEquals, который является именно тем, что RuntimeHelpers.GetHashCode-это для.[конец 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 }
я думал, что пришло время обновить предыдущую реализацию ответов до .Net4.0+, где она упрощается, становясь неродовой благодаря контравариантности на
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); } }
теперь должен существовать только один экземпляр для всех ваших проверок на равенство ссылок вместо одного для каждого типа
T
как было раньше.также вы сохраняете ввод текста, не указывая
T
каждый раз, когда вы хотите использовать это!
разъяснить для тех, кто не знаком с понятиями ковариантность и контравариантность...
class MyClass { ISet<MyClass> setOfMyClass = new HashSet<MyClass>(ReferenceEqualityComparer.Default); }
...будет работать нормально. Это не ограничивается, например,
HashSet<object>
или аналогичный (в. Net4.0).
также для тех, кто задается вопросом, почему
x == y
является ссылочным равенством, это потому, что==
оператор является статическим методом, что означает, что он разрешен во время компиляции, а во время компиляции x и y имеют типobject
так вот он решает к==
операторobject
- это реальные эталонный метод равенства. (На самом делеObject.ReferenceEquals(object, object)
метод-это просто перенаправление на объект, равный оператору.)
вот простая реализация для 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 (вы не должны читать это, если вы заинтересованы в комментариях ниже.)
@AnorZaken посвятил много абзацев трем буквам модификатор. Давайте подведем итоги.
единственный определенный экземпляр
Equals(object,object)
метод реализуетEquals
метод двух объявленных интерфейсов для такого типаIEqualityComparer
и его общий аналогIEqualityComparer<object>
. Сигнатуры идентичны, поэтому это определение удовлетворяет обоим интерфейсам.метод экземпляра
ReferenceEqualityComparer.Equals(object,object)
скрыть статическийobject.Equals(object,object)
метод.без
new
компилятор предупреждает об этом. Что это на самом деле означает?это означает, что если вы хотите вызвать статический
object.Equals
методы, вы не можете вызвать его на экземпляр наReferenceEqualityComparer
. Это большое дело?нет. На самом деле это желательно поведение. Это означает, что если вы хотите позвонить
object.Equals(a,b)
вы не можете сделать это с помощью кода, такого какReferenceEqualityComparer.Default.Equals(a,b)
. Этот код явно запрашивает ссылка равенство -- никто разумно не ожидал бы, что он будет выполнять равенство по умолчанию/значению. Почему бы вам просто не закодировать более явныйobject.Equals(a,b)
в любом случае? Так что использованиеnew
обеспечивает разумное и желательное поведение, и позволяет компиляция без предупреждений.как еще можно подавить предупреждение? Если вы используете
#pragma warning disable 108
/#pragma warning restore 108
тогда результат тот же, что и при использованииnew
, за исключением того, что вы добавили кучу больше шума в свой код.new
достаточно и объясняет намерение более четко для других.в качестве альтернативы вы можете использовать явные реализации для двух интерфейсов
Equals
методы, но если вы использовалиReferenceEqualityComparer.Default.Equals(a,b)
у вас вообще не было бы ссылочного равенства.на самом деле, скрытие статических методов с помощью методов экземпляра редко является проблемой, потому что статические методы разыменованный из спецификатора типа, а не из спецификатора экземпляра. То есть, вы используете
Foo.StaticMethod()
неnew Foo().StaticMethod()
. Вызов статических методов из экземпляров в лучшем случае не нужен, а в худшем-вводит в заблуждение/неверен.кроме того, для сравнения равенства вы редко используете их конкретные типы напрямую. Скорее, вы используете их с API, такими как коллекции.
Итак, хотя это была интересная и порой запутанная дискуссия, она была довольно бесплодной.