是否有使用IEqualityComparer<T>
的默认ReferenceEquals
实施?
EqualityComparer<T>.Default
使用ObjectComparer,它使用object.Equals()
。在我的例子中,对象已经实现了IEquatable<T>
,我需要忽略它并按对象的引用进行比较。
答案 0 :(得分:54)
以防万一没有默认实现,这是我自己的:
编辑280Z28:使用RuntimeHelpers.GetHashCode(object)
的基本原理,你们许多人可能以前从未见过。 :)此方法有两个效果,使其成为此实现的正确调用:
ReferenceEquals
适用于null参数,因此比较器的GetHashCode()实现也是如此。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
}
答案 1 :(得分:14)
我认为现在是时候将之前的答案实施更新为.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
来保存输入!
澄清那些不熟悉Covariance and Contravariance ...
概念的人class MyClass
{
ISet<MyClass> setOfMyClass = new HashSet<MyClass>(ReferenceEqualityComparer.Default);
}
...会工作得很好。这是不限于例如HashSet<object>
或类似的(在.Net4.0中)。
同样对于任何想知道为什么x == y
是引用相等的人,这是因为==
运算符是静态方法,这意味着它在编译时被解析,并且在编译时x和y属于object
类型,因此它解析为==
的{{1}}运算符 - 这是真正的引用相等方法。 (事实上,object
方法只是重定向到对象equals运算符。)
答案 2 :(得分:7)
这是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);
}
编辑(除非您对以下评论感兴趣,否则无需阅读此内容)
@AnorZaken在这里为new
修饰符的三个字母投入了许多段落。让我们总结一下。
单个定义的实例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。
因此虽然这是一个有趣且有时令人困惑的讨论,但它却毫无结果。
答案 3 :(得分:5)
在.NET 5.0中,您现在拥有System.Collections.Generic.ReferenceEqualityComparer
答案 4 :(得分:0)
Microsoft在ObjectReferenceEqualityComparer
中提供System.Data.Entity.Infrastructure
。
只需使用ObjectReferenceEqualityComparer.Default
作为比较器即可。