Domanda

ho notato queste due interfacce, e diverse classi associate, sono state aggiunte in .NET 4. Sembrano un po 'superfluo per me; Ho letto diversi blog su di loro, ma ancora non riesco a capire quale problema risolvono che era difficile prima di .NET 4.

A cosa serve sono IStructuralEquatable e IStructuralComparable?

È stato utile?

Soluzione

Tutti i tipi nella .NET supportano il metodo Object.Equals() che, per impostazione predefinita, a confronto due tipi di l'uguaglianza di riferimento . Tuttavia, a volte, anche desiderabile essere in grado di confrontare due tipi di uguaglianza strutturale .

Il miglior esempio di questo è array, che con NET 4 ora implementano l'interfaccia IStructuralEquatable. Ciò rende possibile distinguere se si confrontano due matrici per l'uguaglianza di riferimento, o "uguaglianza strutturale" - se hanno lo stesso numero di elementi con gli stessi valori in ciascuna posizione. Ecco un esempio:

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

Altri tipi che implementano strutturale uguaglianza / comparabilità includono tuple e tipi anonimi - che sia chiaramente beneficiare della possibilità di eseguire il confronto in base alla loro struttura e il contenuto.

Una domanda non ha chiesto è:

  

Perché abbiamo ci sono IStructuralComparable e IStructuralEquatable quando già   esistono le interfacce IComparable e IEquatable?

La risposta vorrei offrire è che, in generale, è auspicabile distinguere tra i confronti di riferimento e confronti strutturali. E 'normalmente previsto che se si implementa IEquatable<T>.Equals sarà anche ignorare Object.Equals essere coerenti. In questo caso, come è possibile supportare sia di riferimento e l'uguaglianza strutturale?

Altri suggerimenti

Ho avuto la stessa domanda. Quando ho fatto funzionare l'esempio di LBushkin sono rimasto sorpreso di vedere che ho avuto una risposta diversa! Anche se la risposta ha 8 upvotes, è sbagliato. Dopo un sacco di 'reflector'ing, qui è il mio prendere sulle cose.

Alcuni contenitori (array, tuple, tipi anonimi) sostegno IStructuralComparable e IStructuralEquatable.

supporti IStructuralComparable profondo, di default l'ordinamento.
supporti IStructuralEquatable profonde, hashing di default.

{Si noti che i supporti EqualityComparer<T> superficiale (solo 1 livello di contenitore), default hashing.}

Per quanto vedo questo viene esposta solo attraverso la classe StructuralComparisons. L'unico modo che riesco a capire per rendere questo utile è quello di fare una classe helper StructuralEqualityComparer<T> come segue:

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

Ora si può fare un HashSet con gli oggetti che hanno i contenitori all'interno di contenitori all'interno di contenitori.

        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

Possiamo anche fare il nostro gioco proprio contenitore bene con questi altri contenitori mediante l'attuazione di queste interfacce.

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

Ora si può fare un HashSet con gli oggetti che hanno i contenitori all'interno di contenitori personalizzati all'interno di contenitori.

        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

Ecco un altro esempio che illustra un possibile utilizzo delle due interfacce:

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 descrizione del IStructuralEquatable Interface dice (nella sezione "Osservazioni"):

  

L'interfaccia IStructuralEquatable vi permette di implementare i confronti personalizzati per verificare l'uguaglianza strutturale del raccolta di oggetti .

Questa è anche chiaro dal fatto che questo risiede interfaccia nello spazio dei nomi System.Collections.

F # iniziato ad usare loro dato .net 4. (. net 2 è qui )

Queste interfacce sono cruciali per F #

let list1 = [1;5;9] 
let list2 = List.append [1;5] [9]

printfn "are they equal? %b" (list1 = list2)

list1.GetType().GetInterfaces().Dump()

entrare descrizione dell'immagine qui

C # in un libro poche parole:

  

Poiché Array è una classe, gli array sono sempre (se stessi) reference types, indipendentemente   di tipo elemento dell'array. Ciò significa che i risultati dichiarazione arrayB = arrayA   in due variabili che fanno riferimento allo stesso array. Analogamente, due matrici distinte sarà   sempre esito negativo di un test-a meno che l'uguaglianza si utilizza un confronto di uguaglianza personalizzato. Struttura   4.0 ha introdotto una per effettuare un confronto tra elementi in array di cui è possibile   accesso tramite il 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top