代码之家  ›  专栏  ›  技术社区  ›  Yuri Astrakhan

使用referenceEquals的IEqualityComparer<t>

  •  53
  • Yuri Astrakhan  · 技术社区  · 15 年前

    有违约吗 IEqualityComparer<T> 使用的实现 ReferenceEquals ?

    EqualityComparer<T>.Default 使用ObjectComparer,它使用 object.Equals() . 在我的例子中,对象已经实现 IEquatable<T> ,我只需要忽略和比较对象的引用。

    3 回复  |  直到 8 年前
        1
  •  54
  •   Snowbear NicoRiff    13 年前

    如果没有默认的实现,这是我自己的:

    280Z28编辑:使用原理 RuntimeHelpers.GetHashCode(object) 你们中的很多人以前可能没见过。:)此方法有两种效果,使其成为 对的 要求实施:

    1. 当对象为空时返回0。自从 ReferenceEquals 适用于空参数,比较器的getHashCode()实现也适用。
    2. 它叫 Object.GetHashCode() 非实质上。 参考等于 特别忽略 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
    }
    
        2
  •  12
  •   AnorZaken    9 年前

    我认为是时候将以前的answers实现更新到.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> 或类似(in.net4.0)。


    也适用于任何想知道原因的人 x == y 是引用相等,这是因为 == 运算符是静态方法,这意味着它在编译时被解析,编译时x和y是类型 object 所以在这里它决定 = 算子 对象 -那是 真实的 引用相等方法。(事实上 Object.ReferenceEquals(object, object) 方法只是重定向到Object Equals运算符。)

        3
  •  6
  •   Drew Noakes    8 年前

    这是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);
    }
    

    编辑 (除非你对下面的评论感兴趣,否则你不必读这个)

    @阿诺扎肯把许多段落都写在了 new 修改器在这里。让我们总结一下。

    单个已定义实例 Equals(object,object) 方法实现 Equals 此类型的两个已声明接口的方法, IEqualityComparer 和它的一般对应物 IEqualityComparer<object> . 签名是相同的,所以这个定义满足两个接口。

    实例方法 ReferenceEqualityComparer.Equals(object,object) 隐藏 静止的 object.Equals(object,object) 方法。

    没有 新的 编译器对此发出警告。这实际上意味着什么?

    这意味着如果你想调用静态 object.Equals 方法,不能在 实例 属于 ReferenceEqualityComparer . 有什么大不了的吗?

    不,事实上这是理想的行为。意思是如果你想打电话 object.Equals(a,b) 你不能通过诸如 ReferenceEqualityComparer.Default.Equals(a,b) . 那个代码显然是在请求 参考 平等——没有人会合理地期望它执行默认值/值的平等。你为什么不把代码写得更清楚一点呢? 对象.等于(a,b) 反正?所以使用 新的 提供明智和可取的行为,并允许编译而不发出警告。

    你还能怎么抑制这个警告呢?如果使用 #pragma warning disable 108 / #pragma warning restore 108 结果与使用 新的 ,但您在代码中添加了更多的噪声。 新的 更清楚地向其他人解释意图。

    或者,您可以使用两个接口的显式实现 等于 方法,但如果使用 referenceEquityComparer.default.equals(A,B) 你根本就不会有引用平等。

    实际上,用实例方法隐藏静态方法很少是一个问题,因为静态方法是从类型说明符而不是实例说明符取消引用的。也就是说,你用 Foo.StaticMethod() new Foo().StaticMethod() . 从实例调用静态方法充其量是不必要的,最坏是误导性的/不正确的。

    此外,对于相等比较器,很少直接使用它们的具体类型。相反,您可以将它们与API(如集合)一起使用。

    所以,虽然这是一个有趣的,有时令人困惑的讨论,但它是相当没有结果的。