IStructuralEquatable和IStructuralComparable能解决什么问题?

我已经注意到这两个接口,以及几个相关的类,已经被添加到.NET 4中。对我来说,这看起来有些多余。 我已经阅读了几篇关于它们的博客,但是我仍然无法弄清楚.NET 4之前他们解决了哪些棘手的问题。

什么用途是结构可以和结构可比较的?

.NET中的所有types都支持Object.Equals()方法,默​​认情况下,它会比较两种types的引用相等性 。 但是,有时候,也可以比较两种types的结构平等

最好的例子就是数组,它使用.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 

实现结构相等性/可比性的其他types包括元组和匿名types – 这两者都明显受益于基于其结构和内容进行比较的能力。

你没有问的问题是:

当已经存在IComparableIEquatable接口的时候,为什么我们可以IStructuralComparableIStructuralEquatable

我想提供的答案是,一般来说,区分参考比较和结构比较是可取的。 通常预期如果你实现IEquatable<T>.Equals你也将覆盖Object.Equals是一致的。 在这种情况下,你将如何支持参照和结构平等?

我有同样的问题。 当我运行LBushkin的例子时,我惊讶地发现我得到了不同的答案! 即使这个答案有8个投票,这是错误的。 经过很多'reflection',这是我的东西。

某些容器(数组,元组,匿名types)支持IStructuralComparable和IStructuralEquatable。

IStructuralComparable支持深度的默认sorting。
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; } } } 

现在我们可以使用容器内容器内的容器来创build一个HashSet。

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

现在我们可以使用容器内的容器内的容器来创build一个HashSet。

  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接口,您可以实施自定义比较来检查收集对象的结构相等性。

这一点也清楚表明,这个接口驻留在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() 

在这里输入图像描述