Pregunta

Me he dado cuenta de estas dos interfaces, y varias clases asociadas, se han añadido en .NET 4. Se parecen un poco superfluo para mí; He leído varios blogs sobre ellos, pero todavía no puedo averiguar cuál es el problema que resuelven que era difícil antes de .NET 4.

¿De qué sirven IStructuralEquatable y IStructuralComparable?

¿Fue útil?

Solución

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

El mejor ejemplo de esto es arrays, que con .NET 4 ahora implementan la interfaz IStructuralEquatable. Esto hace que sea posible distinguir si usted está comparando dos matrices para la igualdad de referencia o de "igualdad estructural" - si tienen el mismo número de elementos con los mismos valores en cada posición. He aquí 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 estructurales que ponen en práctica la igualdad / comparabilidad incluyen tuplas y los tipos anónimos - los que ambos se benefician claramente de la capacidad de realizar la comparación en función de su estructura y contenido.

Una pregunta que no pidió es:

  

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

La respuesta que iba a ofrecer es que, en general, es conveniente diferenciar entre las comparaciones de referencia y comparaciones estructurales. Se espera normalmente que si se implementa IEquatable<T>.Equals también se anulará Object.Equals ser consistente. En este caso, ¿cómo soportar tanto de referencia y la igualdad estructural?

Otros consejos

Yo tenía la misma pregunta. Cuando me encontré con el ejemplo de LBushkin Me sorprendió ver que me dieron una respuesta diferente! A pesar de que la respuesta tiene 8 upvotes, es erróneo. Después de un montón de 'reflector'ing, aquí es mi opinión sobre las cosas.

Ciertos recipientes (arrays, tuplas, tipos anónimos) apoyo IStructuralComparable y IStructuralEquatable.

soportes IStructuralComparable profunda, orden predeterminado.
soportes IStructuralEquatable profundas, hash por defecto.

{Nota que los soportes EqualityComparer<T> poco profunda (sólo 1 nivel de contenedor), hashing predeterminada.}

Por lo que yo veo esto sólo se expone a través de la clase StructuralComparisons. La única manera que puedo imaginar para hacer de este útil es hacer una clase StructuralEqualityComparer<T> ayudante de la 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 los objetos que tienen contenedores dentro de los contenedores dentro de los 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 nuestro propio juego recipiente 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 los objetos que tienen contenedores dentro de los contenedores personalizados dentro de los 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

Aquí es 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

La descripción de la Interfaz IStructuralEquatable dice (en la sección "Comentarios"):

  

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

Esto también se pone de manifiesto por el hecho de que esta interfaz reside en el espacio de nombres System.Collections.

F # empezó a utilizarlos porque .NET 4. (. net 2 es 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()

introducir descripción de la imagen aquí

C # en un libro pocas palabras:

  

Debido a que la matriz es una clase, las matrices son siempre (sí) reference types, sin tener en cuenta   de tipo de elemento de la matriz. Esto significa que los resultados comunicado arrayB = arrayA   en dos variables que hacen referencia a la misma matriz. Del mismo modo, dos matrices distintas voluntad   Siempre fallar una prueba, a menos que la igualdad se utiliza un comparador de igualdad personalizado. Marco de referencia   4.0 introdujo una para el propósito de comparar los elementos de las matrices las cuales se puede   Acceso a través del tipo 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top