Which problem solves IStructuralEquatable and IStructuralComparable?

I noticed that these two interfaces and several related classes were added in .NET 4. They seem superfluous to me; I read several blogs about them, but I still can’t understand what problems they solve, which was difficult for .NET 4.

What are the IStructuralEquatable and IStructuralComparable ?

+54
equality iequalitycomparer icomparable
Aug 31 '10 at 14:09
source share
6 answers

All types in .NET support the Object.Equals() method, which by default compares two types for referential equality . However, sometimes it is also desirable to be able to compare the two types for structural equality .

The best example of this are arrays that, with .NET 4, now implement the IStructuralEquatable interface. This allows you to distinguish whether you are comparing two arrays for reference equality or "structural equality" - whether they have the same number of elements with the same values ​​in each position. Here is an example:

 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 

Other types that implement structural equality / comparability include tuples and anonymous types that clearly benefit from being able to perform comparisons based on their structure and content.

The question you did not ask is:

Why do we have IStructuralComparable and IStructuralEquatable when the IComparable and IEquatable interfaces already exist?

The answer that I would suggest is that, on the whole, it is desirable to distinguish between comparative comparisons and structural comparisons. It was usually expected that if you implement IEquatable<T>.Equals , you will also override Object.Equals to be consistent. In this case, how would you support both referential and structural equality?

+45
Aug 31 '10 at 14:25
source share

I had the same question. When I launched the example of L. Bushkin, I was surprised to see that I have a different answer! Despite the fact that this answer has 8 priorities, this is incorrect. After many "reflex", here is my lesson.

Some containers (arrays, tuples, anonymous types) support IStructuralComparable and IStructuralEquatable.

IStructuralComparable supports deep sorting by default.
IStructuralEquatable supports deep hashing by default.

{Note that EqualityComparer <T> supports shallow (only one container level), hashing by default.}

As far as I understand, this is displayed only through the StructuralComparisons class. The only way I can do this is to make the helper class StructuralEqualityComparer<T> as follows:

  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; } } } 

Now we can create a HashSet with elements containing containers in containers inside containers.

  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 

We can also do our own container well with these other containers by implementing these interfaces.

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

Now we can create a HashSet with elements containing containers in custom containers in containers.

  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 
+15
Apr 08 2018-11-11T00:
source share

Here is another example illustrating the possible use of two interfaces:

 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 
+3
Jul 14 '11 at 8:45
source share

The IStructuralEquatable interface description states (in the "Remarks" section):

The IStructuralEquatable interface allows you to perform custom comparisons to verify the structural equality of collection objects .

This also becomes apparent due to the fact that this interface is in the System.Collections namespace.

+2
Feb 13 '16 at 23:16
source share

F # started using them with .net 4. ( .NET 2 here )

These interfaces are critical to 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

0
Sep 29 '17 at 18:42 on
source share

C # in a nutshell :

Because Array is a class, arrays are always (by themselves) reference types , regardless of the type of element in the array. This means that the arrayB = arrayA results in two variables that reference the same array. Similarly, two different arrays will not always pass the equality test, unless you use a custom equality comparator. Framework 4.0 introduced one to compare elements in arrays, which you can access through the StructuralComparisons type.

 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 
0
Apr 6 '19 at 3:59
source share



All Articles