سؤال

هل هناك افتراضي IEqualityComparer<T> التنفيذ الذي يستخدم ReferenceEquals?

EqualityComparer<T>.Default يستخدم Objectcomparer، والذي يستخدم object.Equals(). وبعد في حالتي، يتم تنفيذ الكائنات بالفعل IEquatable<T>, ، والتي أحتاج إلى تجاهلها ومقارنتها بمرجع الكائن فقط.

هل كانت مفيدة؟

المحلول

فقط في حالة عدم وجود تطبيق افتراضي، هذا هو:

تحرير بواسطة 280Z28: الأساس المنطقي للاستخدام RuntimeHelpers.GetHashCode(object), ، والتي ربما لم ير الكثير منكم من قبل. :) هذه الطريقة لها اثنان من آثار تجعلها صيح دعوة لهذا التنفيذ:

  1. ترجع 0 عندما يكون الكائن فارغا. حيث ReferenceEquals يعمل معلمات فارغة، لذلك يجب أن يكون تنفيذ المقارنة ل GETHASHCODE ().
  2. يدعو Object.GetHashCode() غير تقريبا. ReferenceEquals يتجاهل خصيصا أي تجاوزات Equals, ، لذلك يجب أن يستخدم تنفيذ Gethashcode () طريقة خاصة تتطابق مع تأثير المركبات، وهو بالضبط ما هو 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);
}

تعديل (ليس عليك قراءة هذا إلا إذا كنت مهتما بالتعليقات أدناه)

@ 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(). وبعد إن استدعاء الطرق الثابتة من الحالات غير ضرورية في أحسن الأحوال وتضليل / غير صحيح في أسوأ الأحوال.

علاوة على ذلك، من أجل مقارنات المساواة، نادرا ما تستخدم أنواع الخرسانة الخاصة بهم مباشرة. بدلا من ذلك، يمكنك استخدامها مع Apis مثل المجموعات.

لذلك، في حين كان هذا مثيرا للاهتمام وفي بعض الأحيان مناقشة مربكة، فقد كانت غير مثمرة إلى حد ما.

قدمت مايكروسوفت ObjectReferenceEqualityComparer في System.Data.Entity.Infrastructureوبعد مجرد استخدام ObjectReferenceEqualityComparer.Default مقارنة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top