سؤال

لقد لاحظت هاتين الواجهتين ، وتم إضافة عدة فئتين مرتبطة في .NET 4. يبدو أنه لا لزوم له بالنسبة لي ؛ لقد قرأت العديد من المدونات عنها ، لكن ما زلت لا أستطيع معرفة المشكلة التي حلها والتي كانت صعبة قبل .NET 4.

ما الفائدة IStructuralEquatable و IStructuralComparable?

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

المحلول

جميع الأنواع في .NET دعم Object.Equals() الطريقة التي ، بشكل افتراضي ، تقارن نوعين ل المساواة المرجعية. ومع ذلك ، في بعض الأحيان ، من المستحسن أيضًا أن تكون قادرًا على مقارنة نوعين المساواة الهيكلية.

أفضل مثال على ذلك هو المصفوفات ، والتي مع .NET 4 تنفذ الآن IStructuralEquatable واجهه المستخدم. هذا يجعل من الممكن التمييز بين ما إذا كنت تقارن صفيفتين للمساواة المرجعية ، أو لـ "المساواة الهيكلية" - سواء كان لديهم نفس عدد العناصر مع نفس القيم في كل موقف. هذا مثال:

int[] array1 = new int[] { 1, 5, 9 };
int[] array2 = new int[] { 1, 5, 9 };

// using reference comparison...
Console.WriteLine( array1.Equals( array2 ) ); // outputs false

// now using the System.Array implementation of IStructuralEquatable
Console.WriteLine( StructuralComparisons.StructuralEqualityComparer.Equals( array1, array2 ) ); // outputs true

تشمل الأنواع الأخرى التي تنفذ المساواة الهيكلية/قابلية المقارنة tuples وأنواع مجهولة - والتي تستفيد بوضوح من القدرة على إجراء مقارنة استنادًا إلى هيكلها ومحتواه.

سؤال لم تطرحه هو:

لماذا لدينا IStructuralComparable و IStructuralEquatable عندما يكون هناك بالفعل IComparable و IEquatable واجهات؟

الجواب الذي أقدمه هو أنه ، بشكل عام ، من المستحسن التمييز بين المقارنات المرجعية والمقارنات الهيكلية. من المتوقع عادة أنه إذا كنت تنفذ IEquatable<T>.Equals سوف تتغلب أيضا Object.Equals أن تكون متسقة. في هذه الحالة ، كيف تدعم كل من المساواة المرجعية والهيكلية؟

نصائح أخرى

لدي نفس السؤال. عندما ركضت مثال Lbushkin ، فوجئت برؤية أنني حصلت على إجابة مختلفة! على الرغم من أن هذه الإجابة تحتوي على 8 صغار ، إلا أنه من الخطأ. بعد الكثير من "العاكس" ، ها هو رأيي في الأشياء.

بعض الحاويات (المصفوفات ، tuples ، أنواع مجهولة) تدعم isructuralparable و isructuralequatable.

يدعم istructuralparable القابلة للفرز العميق.
تدعم isTructUralequatable التجزئة العميقة الافتراضية.

{لاحظ أن EqualityComparer<T> يدعم الضحلة (مستوى حاوية واحد فقط) ، التجزئة الافتراضية.}

بقدر ما أرى أن هذا يتعرض فقط من خلال فئة structuralcomparisons. الطريقة الوحيدة التي يمكنني من خلالها اكتشاف ذلك مفيدًا هي صنع ملف StructuralEqualityComparer<T> فئة المساعدة على النحو التالي:

    public class StructuralEqualityComparer<T> : IEqualityComparer<T>
    {
        public bool Equals(T x, T y)
        {
            return StructuralComparisons.StructuralEqualityComparer.Equals(x,y);
        }

        public int GetHashCode(T obj)
        {
            return StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
        }

        private static StructuralEqualityComparer<T> defaultComparer;
        public static StructuralEqualityComparer<T> Default
        {
            get
            {
                StructuralEqualityComparer<T> comparer = defaultComparer;
                if (comparer == null)
                {
                    comparer = new StructuralEqualityComparer<T>();
                    defaultComparer = comparer;
                }
                return comparer;
            }
        }
    }

الآن يمكننا صنع مجموعة تجزئة بها عناصر تحتوي على حاويات داخل الحاويات داخل الحاويات.

        var item1 = Tuple.Create(1, new int[][] { new int[] { 1, 2 }, new int[] { 3 } });
        var item1Clone = Tuple.Create(1, new int[][] { new int[] { 1, 2 }, new int[] { 3 } });
        var item2 = Tuple.Create(1, new int[][] { new int[] { 1, 3 }, new int[] { 3 } });

        var set = new HashSet<Tuple<int, int[][]>>(StructuralEqualityComparer<Tuple<int, int[][]>>.Default);
        Console.WriteLine(set.Add(item1));      //true
        Console.WriteLine(set.Add(item1Clone)); //false
        Console.WriteLine(set.Add(item2));      //true

يمكننا أيضًا جعل الحاوية الخاصة بنا تلعب بشكل جيد مع هذه الحاويات الأخرى من خلال تنفيذ هذه الواجهات.

public class StructuralLinkedList<T> : LinkedList<T>, IStructuralEquatable
    {
        public bool Equals(object other, IEqualityComparer comparer)
        {
            if (other == null)
                return false;

            StructuralLinkedList<T> otherList = other as StructuralLinkedList<T>;
            if (otherList == null)
                return false;

            using( var thisItem = this.GetEnumerator() )
            using (var otherItem = otherList.GetEnumerator())
            {
                while (true)
                {
                    bool thisDone = !thisItem.MoveNext();
                    bool otherDone = !otherItem.MoveNext();

                    if (thisDone && otherDone)
                        break;

                    if (thisDone || otherDone)
                        return false;

                    if (!comparer.Equals(thisItem.Current, otherItem.Current))
                        return false;
                }
            }

            return true;
        }

        public int GetHashCode(IEqualityComparer comparer)
        {
            var result = 0;
            foreach (var item in this)
                result = result * 31 + comparer.GetHashCode(item);

            return result;
        }

        public void Add(T item)
        {
            this.AddLast(item);
        }
    }

الآن يمكننا صنع مجموعة من الأشياء التي تحتوي على حاويات داخل حاويات مخصصة داخل الحاويات.

        var item1 = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 2 }, new int[] { 3 } });
        var item1Clone = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 2 }, new int[] { 3 } });
        var item2 = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 3 }, new int[] { 3 } });

        var set = new HashSet<Tuple<int, StructuralLinkedList<int[]>>>(StructuralEqualityComparer<Tuple<int, StructuralLinkedList<int[]>>>.Default);
        Console.WriteLine(set.Add(item1));      //true
        Console.WriteLine(set.Add(item1Clone)); //false
        Console.WriteLine(set.Add(item2));      //true

فيما يلي مثال آخر يوضح استخدامًا محتملًا للواجهات:

var a1 = new[] { 1, 33, 376, 4};
var a2 = new[] { 1, 33, 376, 4 };
var a3 = new[] { 2, 366, 12, 12};

Debug.WriteLine(a1.Equals(a2)); // False
Debug.WriteLine(StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)); // True

Debug.WriteLine(StructuralComparisons.StructuralComparer.Compare(a1, a2)); // 0
Debug.WriteLine(StructuralComparisons.StructuralComparer.Compare(a1, a3)); // -1

وصف IStructuralEquatable واجهه المستخدم يقول (في قسم "التصريحات"):

ال IStructuralEquatable تمكنك الواجهة من تنفيذ مقارنات مخصصة للتحقق من المساواة الهيكلية لـ كائنات جمع.

ويوضح ذلك أيضًا من خلال حقيقة أن هذه الواجهة موجودة في System.Collections مساحة الاسم.

بدأ F# استخدامها منذ .NET 4. ( .NET 2 هنا)

هذه الواجهات حاسمة لـ F#

let list1 = [1;5;9] 
let list2 = List.append [1;5] [9]

printfn "are they equal? %b" (list1 = list2)

list1.GetType().GetInterfaces().Dump()

enter image description here

ج# باختصار الكتاب:

لأن المصفوفة فئة ، فإن المصفوفات دائمًا (نفسها) reference types, ، بغض النظر عن نوع عنصر الصفيف. هذا يعني أن البيان arrayB = arrayA النتائج في متغيرين يشيرون إلى نفس الصفيف. وبالمثل ، ستفشل صفيفتان متميزتان دائمًا في اختبار المساواة - ما لم تكن تستخدم مقارنًا مخصصًا للمساواة. قدم Framework 4.0 واحدة لغرض مقارنة العناصر في المصفوفات التي يمكنك الوصول إليها عبر StructuralComparisons يكتب.

object[] a1 = { "string", 123, true};
object[] a2 = { "string", 123, true};

Console.WriteLine(a1 == a2);               // False
Console.WriteLine(a1.Equals(a2));          // False

IStructuralEquatable se1 = a1;
Console.WriteLine(se1.Equals(a2, StructuralComparisons.StructuralEqualityComparer));    // True
Console.WriteLine(StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2));     // True

object[] a3 = {"string", 123, true};
object[] a4 = {"string", 123, true};
object[] a5 = {"string", 124, true};

IStructuralComparable se2 = a3;
Console.WriteLine(se2.CompareTo(a4, StructuralComparisons.StructuralComparer));    // 0
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a3, a4));       // 0
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a4, a5));       // -1
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a5, a4));       // 1
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top