.net equality icomparable iequalitycomparer

.net - ¿Qué problema resuelve IStructuralEquatable y IStructuralComparable?



equality icomparable (5)

Me he dado cuenta de que estas dos interfaces, y varias clases asociadas, se han agregado en .NET 4. Me parecen un poco superfluas; He leído varios blogs sobre ellos, pero todavía no puedo descifrar qué problema solucionaron antes .NET 4.

¿Para qué sirven IStructuralEquatable y IStructuralComparable ?


Aquí hay otro ejemplo que ilustra un posible uso de las dos 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


En la descripción de la Interfaz IStructuralEquatable Microsoft dice claramente (en la sección "Observaciones"):

La interfaz IStructuralEquatable permite implementar comparaciones personalizadas para verificar la igualdad estructural de los objetos de colección .

Esto también queda claro por el hecho de que esta interfaz reside en el espacio de nombres System.Collections .


F # comenzó a usarlos desde .net 4. ( .net 2 está aquí )

Estas interfaces son cruciales para F #

let list1 = [1;5;9] let list2 = List.append [1;5] [9] printfn "are they equal? %b" (list1 = list2) list1.GetType().GetInterfaces().Dump()


Tenía la misma pregunta. Cuando ejecuté el ejemplo de LBushkin, me sorprendí al ver que obtuve una respuesta diferente. A pesar de que esa respuesta tiene 8 votos ascendentes, está mal. Después de un montón de ''reflector'', aquí está mi opinión sobre las cosas.

Ciertos contenedores (matrices, tuplas, tipos anónimos) admiten IStructuralComparable e IStructuralEquatable.

IStructuralComparable admite clasificación profunda y predeterminada.
IStructuralEquatable admite hashing profundo y predeterminado.

{Tenga en cuenta que EqualityComparer<T> admite poco profundo (solo 1 nivel de contenedor), hashing predeterminado.}

Por lo que veo, esto solo está expuesto a través de la clase StructuralComparisons. La única forma en que puedo averiguar para hacer esto útil es hacer una clase de ayuda StructuralEqualityComparer<T> siguiente manera:

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

Ahora podemos hacer un HashSet con elementos que tienen contenedores dentro de contenedores dentro de contenedores.

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

También podemos hacer que nuestro propio contenedor funcione bien con estos otros contenedores mediante la implementación de estas 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); } }

Ahora podemos hacer un HashSet con elementos que tienen contenedores dentro de contenedores personalizados dentro de contenedores.

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


Todos los tipos en .NET admiten el método Object.Equals() que, de forma predeterminada, compara dos tipos para la igualdad de referencia . Sin embargo, a veces, también es deseable poder comparar dos tipos para la igualdad estructural .

El mejor ejemplo de esto son las matrices, que con .NET 4 ahora implementan la interfaz IStructuralEquatable . Esto hace posible distinguir si está comparando dos matrices para la igualdad de referencia, o para la "igualdad estructural", si tienen el mismo número de elementos con los mismos valores en cada posición. Aquí hay un ejemplo:

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

Otros tipos que implementan la igualdad / comparabilidad estructural incluyen tuplas y tipos anónimos, que se benefician claramente de la capacidad de realizar una comparación basada en su estructura y contenido.

Una pregunta que no hizo es:

¿Por qué tenemos IStructuralComparable e IStructuralEquatable cuando ya existen las interfaces IComparable e IEquatable ?

La respuesta que ofrecería es que, en general, es deseable diferenciar entre las comparaciones de referencia y las comparaciones estructurales. Normalmente se espera que si implementa IEquatable<T>.Equals también anule Object.Equals para que sea coherente. En este caso, ¿cómo apoyarías tanto la referencia como la igualdad estructural?