Development Tip

IEqualityComparer

yourdevel 2020. 12. 25. 10:35
반응형

IEqualityComparer ReferenceEquals를 사용하는


IEqualityComparer<T>사용 하는 기본 구현이 ReferenceEquals있습니까?

EqualityComparer<T>.Default를 사용하는 ObjectComparer를 사용합니다 object.Equals(). 제 경우에는 객체가 이미 구현되어 있으므로 IEquatable<T>객체의 참조로만 무시하고 비교해야합니다.


기본 구현이없는 경우를 대비하여 다음과 같습니다.

280Z28에 의해 편집 :를 사용하는 이유. RuntimeHelpers.GetHashCode(object)많은 분들이 이전에 본 적이 없을 것입니다. :)이 메서드는 이 구현에 대한 올바른 호출을 만드는 두 가지 효과가 있습니다 .

  1. 객체가 null이면 0을 반환합니다. 이후 ReferenceEquals널 매개 변수에 대한 작품, 그래서해야 GetHashCode의 비교 자 구현 ().
  2. Object.GetHashCode()가상으로 호출 하지 않습니다. ReferenceEquals특히는의 모든 재정의를 무시 Equals하므로 GetHashCode () 구현에서는 RuntimeHelpers.GetHashCode의 용도 인 ReferenceEquals 효과와 일치하는 특수 메서드를 사용해야합니다.

[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이것을 사용하고 싶을 때마다 지정하지 않아도 되므로 타이핑을 절약 할 수 있습니다!


To clarify for those who are not familiar with the concepts of Covariance and Contravariance...

class MyClass
{
    ISet<MyClass> setOfMyClass = new HashSet<MyClass>(ReferenceEqualityComparer.Default);
}

...will work just fine. This is not limited to e.g. HashSet<object> or similar (in .Net4.0).


Also for anyone wondering why x == y is reference equality, it is because the ==operator is a static method, which means it is resolved at compile-time, and at compile-time x and y are of type object so here it resolves to the ==operator of object - which is the real reference equality method. (In fact the Object.ReferenceEquals(object, object) method is simply a redirect to the object equals operator.)


Here's a simple implementation for 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 (You don't have to read this unless you're interested in the comments below)

@AnorZaken devoted many paragraphs to the three letters of the new modifier here. Let's summarise.

The single defined instance Equals(object,object) method implements the Equals method of the two declared interfaces for this type, IEqualityComparer and its generic counterpart IEqualityComparer<object>. The signatures are identical, so this definition satisfies both interfaces.

The instance method ReferenceEqualityComparer.Equals(object,object) hides the static object.Equals(object,object) method.

Without new the compiler warns about this. What does this actually mean?

It means that if you want to call the static object.Equals methods, you cannot call it on an instance of ReferenceEqualityComparer. Is this a big deal?

No. In fact it's desired behaviour. It means that if you want to call object.Equals(a,b) you cannot do it via code such as ReferenceEqualityComparer.Default.Equals(a,b). That code is clearly requesting reference equality -- no one would reasonably expect it to perform default/value equality. Why wouldn't you just code the more explicit object.Equals(a,b) anyway? So the use of new provides sensible and desirable behaviour, and allows compilation with no warnings.

How else could you suppress the warning? If you use a #pragma warning disable 108/#pragma warning restore 108 then the outcome is the same as using new, except you've added a bunch more noise to your code. new suffices and explains the intent more clearly to others.

Alternatively you could use explicit implementations for the two interface Equals methods, but then if you used ReferenceEqualityComparer.Default.Equals(a,b) you wouldn't have reference equality at all.

In reality, hiding static methods with instance methods is rarely a problem because static methods are dereferenced from a type specifier, not an instance specifier. That is, you use Foo.StaticMethod() not new Foo().StaticMethod(). Calling static methods from instances is unnecessary at best and misleading/incorrect at worst.

Further, for equality comparers, you rarely use their concrete types directly. Rather, you use them with APIs such as collections.

So whilst this was an interesting and at times confusing discussion, it was rather fruitless.


Microsoft provide ObjectReferenceEqualityComparer in System.Data.Entity.Infrastructure. Just use ObjectReferenceEqualityComparer.Default as comparer.

ReferenceURL : https://stackoverflow.com/questions/1890058/iequalitycomparert-that-uses-referenceequals

반응형