Yur*_*rik 57 .net c# iequalitycomparer referenceequals
是否有使用的默认IEqualityComparer<T>实现ReferenceEquals?
EqualityComparer<T>.Default使用ObjectComparer,它使用object.Equals().在我的例子中,对象已经实现IEquatable<T>,我需要忽略并仅通过对象的引用进行比较.
Yur*_*rik 56
为了防止没有默认实现,这是我自己的:
编辑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
}
Run Code Online (Sandbox Code Playgroud)
Ano*_*ken 16
我认为现在是时候将以前的答案实施更新到.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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在只需要为所有引用相等性检查存在一个实例,而不是T像以前那样为每种类型存在一个实例.
您也可以通过不必T每次都要使用它来保存输入!
澄清那些不熟悉协方差和反演的概念......
class MyClass
{
ISet<MyClass> setOfMyClass = new HashSet<MyClass>(ReferenceEqualityComparer.Default);
}
Run Code Online (Sandbox Code Playgroud)
......会工作得很好.这并不限定于例如HashSet<object>或相似的(在.Net4.0).
此外,任何人想知道为什么x == y是引用相等,这是因为==运营商是一个静态方法,这意味着它在编译时得到解决,并在编译时x和y型的object所以在这里解析为==运营商object-这是真正的参考平等方法.(实际上该Object.ReferenceEquals(object, object)方法只是重定向到对象equals运算符.)
这是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);
}
Run Code Online (Sandbox Code Playgroud)
编辑(除非您对以下评论感兴趣,否则您不必阅读此内容)
@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(如集合)一起使用.
因此,尽管这是一个有趣且有时令人困惑的讨论,但它却毫无结果.
| 归档时间: |
|
| 查看次数: |
7115 次 |
| 最近记录: |