ما هي المشكلة التي يحلها isTructUralequatable و isructuralparable؟
-
25-09-2019 - |
سؤال
لقد لاحظت هاتين الواجهتين ، وتم إضافة عدة فئتين مرتبطة في .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()
ج# باختصار الكتاب:
لأن المصفوفة فئة ، فإن المصفوفات دائمًا (نفسها)
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