Was Problem nicht IStructuralEquatable und IStructuralComparable lösen?
-
25-09-2019 - |
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
?
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
undIStructuralEquatable
, wenn es bereits existiert dasIComparable
undIEquatable
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 AussagearrayB = 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 denStructuralComparisons
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