Frage

Ich habe diese beiden Schnittstellen bemerkt und mehrere zugehörigen Klassen haben in .NET 4. Sie scheinen ein wenig überflüssig mir hinzugefügt; Ich habe über sie mehrere Blogs lesen, aber ich kann immer noch nicht herausfinden, was Problem, das sie lösen das war heikel, bevor .NET 4.

Was Nutzung ist IStructuralEquatable und IStructuralComparable?

War es hilfreich?

Lösung

Alle Typen in .NET unterstützen die Object.Equals() Methode, die in der Standardeinstellung vergleicht zwei Typen für Referenz Gleichheit . manchmal ist es jedoch auch wünschenswert, zwei Typen für vergleichen Strukturgleichheit .

Das beste Beispiel hierfür ist Arrays, die mit .NET 4 jetzt dem IStructuralEquatable-Schnittstelle implementieren. Dies macht es möglich, zu unterscheiden, ob Sie zwei Arrays als Referenz Gleichheit vergleichen, oder für „strukturelle Gleichheit“ - ob sie die gleiche Anzahl von Elementen mit den gleichen Werten in jeder Position haben. Hier ein Beispiel:

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

Andere Arten, die Strukturgleichheit / Vergleichbarkeit implementieren sind Tupeln und anonyme Typen - die beide deutlich von der Möglichkeit profitieren Vergleich auszuführen, basierend auf ihre Struktur und Inhalt.

Eine Frage, die Sie nicht gefragt ist:

  

Warum haben wir IStructuralComparable und IStructuralEquatable, wenn es bereits   existiert das IComparable und IEquatable Schnittstellen?

Die Antwort, die ich anbieten würde, ist, dass im Allgemeinen, es wünschenswert ist zwischen Referenz Vergleichen und strukturellen Vergleichen zu unterscheiden. Es ist normalerweise nicht zu erwarten, dass, wenn Sie IEquatable<T>.Equals implementieren Sie auch Object.Equals konsistent sein überschreiben. In diesem Fall, wie würden Sie sowohl Referenz- und strukturelle Gleichheit unterstützen?

Andere Tipps

Ich hatte die gleiche Frage. Als ich LBushkin das Beispiel lief war ich überrascht zu sehen, dass ich eine andere Antwort bekommen! Auch wenn diese Antwort 8 upvotes hat, ist es falsch. Nach viel ‚reflector'ing, hier ist mein nehmen auf die Dinge.

Bestimmte Behälter (Arrays, Tupeln, anonyme Typen) Unterstützung IStructuralComparable und IStructuralEquatable.

IStructuralComparable unterstützt tief, Standardsortierung.
IStructuralEquatable unterstützt tief, Standard-Hashing.

{Beachten Sie, dass EqualityComparer<T> Träger flach (nur 1 Container-Ebene), Standard-Hashing.}

Soweit ich sehe dies nur durch die StructuralComparisons Klasse ausgesetzt ist. Die einzige Art, wie ich herausfinden kann dies nützlich zu machen, ist eine StructuralEqualityComparer<T> Helfer Klasse wie folgt zu machen:

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

Jetzt können wir eine HashSet machen mit Gegenständen Behälter in Containern innerhalb Behältern mit.

        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

Wir können auch unsere eigenen Container spielen gut mit diesen anderen Behältern machen, indem sie diese Schnittstellen implementieren.

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

Jetzt können wir eine HashSet machen mit Gegenständen Behälter in benutzerdefinierten Container in Behältern mit.

        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

Hier ist ein weiteres Beispiel, das eine mögliche Nutzung der beiden Schnittstellen zeigt:

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

Die Beschreibung der IStructuralEquatable Schnittstelle sagt (im Abschnitt "Hinweise"):

  

Die IStructuralEquatable Schnittstelle ermöglicht es Ihnen, kundenspezifische Vergleiche implementieren für die strukturelle Gleichstellung von Sammlung zu überprüfen Objekte .

Dies wird auch deutlich gemacht, durch die Tatsache, dass diese Schnittstelle liegt in der System.Collections Namespace.

F # mit ihnen begonnen, da .net 4. (. netto-2 ist hier )

Diese Schnittstellen sind entscheidend für F #

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

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

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

C # in aller Kürze Buch:

  

Weil Array eine Klasse ist, Arrays sind immer (selbst) reference types, unabhängig   der Elementtypen des Arrays. Dies bedeutet, dass die Aussage arrayB = arrayA Ergebnisse   in zwei Variablen, die die gleiche Anordnung verweisen. In ähnlicher Weise zwei verschiedene Arrays werden   immer scheitern einen Gleichheitstest-es sei denn, Sie eine benutzerdefinierte Gleichheitsvergleich verwendet werden. Rahmen   4.0 eingeführt man zum Zwecke der Elemente in Arrays zu vergleichen denen Sie   Zugang über den StructuralComparisons Typ.

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top