代码之家  ›  专栏  ›  技术社区  ›  thecoop

IStructuralQuatable和IStructuralComparable解决了什么问题?

  •  54
  • thecoop  · 技术社区  · 15 年前

    我注意到这两个接口和几个相关的类已经添加到.NET4中。它们对我来说似乎有点多余;我读过几篇关于它们的博客,但我仍然不知道它们解决的问题在.NET 4之前是多么棘手。

    有什么用? IStructuralEquatable IStructuralComparable ?

    6 回复  |  直到 7 年前
        1
  •  45
  •   Jeppe Stig Nielsen    13 年前

    .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
    

    实现结构平等/可比性的其他类型包括元组和匿名类型——这两种类型都明显受益于根据结构和内容执行比较的能力。

    你没有问的问题是:

    为什么我们有 IStructuralComparable 可伸缩的 当已经在那里的时候 存在 IComparable IEquatable 接口?

    我会给出的答案是,一般来说,最好区分参考比较和结构比较。通常情况下,如果您实现 IEquatable<T>.Equals 您还将覆盖 Object.Equals 保持一致。在这种情况下,您如何支持引用和结构平等?

        2
  •  14
  •   jyoung    15 年前

    我也有同样的问题。当我运行Lbushkin的例子时,我惊讶地发现我得到了一个不同的答案!尽管这个答案有8个赞成票,但它是错误的。经过多次的“反射”之后,这是我对事物的看法。

    某些容器(数组、元组、匿名类型)支持IStructuralComparable和IStructuralQuatable。

    IStructuralComparable支持深度默认排序。
    IStructuraleQuatable支持深度默认散列。

    {注意 EqualityComparer<T> 支持Shallow(仅1个容器级别),默认哈希。

    据我所见,这只通过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
    
        3
  •  3
  •   Marc Sigrist    15 年前

    下面是另一个示例,说明了两个接口的可能用法:

    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
    
        4
  •  0
  •   Olivier Jacot-Descombes    10 年前

    在描述中 IStructuralEquatable Interface 微软明确表示(在“备注”部分):

    这个 可伸缩的 接口使您能够实现自定义比较,以检查 集合对象 .

    这个接口位于 System.Collections 命名空间。

        5
  •  0
  •   Rm558    8 年前

    f从.NET 4开始使用它们。( .net 2 is here )

    这些接口对F至关重要#

    let list1=[1;5;9]
    让list2=list.append[1;5][9]
    
    printfn“它们相等吗?%B”(列表1=列表2)
    
    列表1.getType().getInterfaces().dump())
    < /代码> 
    
    

    )

    这些接口对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

        6
  •  0
  •   Sina Lotfi    7 年前

    C# in a nutshell 书:

    因为数组是一个类,所以数组总是(本身) reference types 不管 数组的元素类型。这意味着 arrayB = arrayA 结果 在引用同一数组的两个变量中。同样,两个不同的数组将 除非使用自定义的相等比较器,否则必须通过相等测试。框架 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
    
    推荐文章