Dies ist etwas, das ich bis heute nicht bemerkt hatte. Offensichtlich verursacht die .NET-Implementierung der vielbenutzten Tuple-Klassen (Tuple<T>
, Tuple<T1, T2>
usw.) bei der Ausführung gleichheitsbasierter Operationen die Boxstrafen für die Werttypen..NET Tuple und Gleichwertige Leistung
Hier ist, wie die Klasse Art im Rahmen (Quelle über ILSpy) implementiert:
public class Tuple<T1, T2> : IStructuralEquatable
{
public T1 Item1 { get; private set; }
public T2 Item2 { get; private set; }
public Tuple(T1 item1, T2 item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
public override bool Equals(object obj)
{
return this.Equals(obj, EqualityComparer<object>.Default);
}
public override int GetHashCode()
{
return this.GetHashCode(EqualityComparer<object>.Default);
}
public bool Equals(object obj, IEqualityComparer comparer)
{
if (obj == null)
{
return false;
}
var tuple = obj as Tuple<T1, T2>;
return tuple != null
&& comparer.Equals(this.Item1, tuple.Item1)
&& comparer.Equals(this.Item2, tuple.Item2);
}
public int GetHashCode(IEqualityComparer comparer)
{
int h1 = comparer.GetHashCode(this.Item1);
int h2 = comparer.GetHashCode(this.Item2);
return (h1 << 5) + h1^h2;
}
}
Das Problem, das ich sehe, ist es für Equals
Anrufe eines zweistufiger Boxen-Unboxing, sagt verursacht, ein, an der comparer.Equals
welche boxen den artikel, zwei, die EqualityComparer<object>
ruft die nicht-generischeEquals
die wiederum intern haben, um das einzelteil zu orginal type.
Stattdessen warum würden sie nicht so etwas wie:
public override bool Equals(object obj)
{
var tuple = obj as Tuple<T1, T2>;
return tuple != null
&& EqualityComparer<T1>.Default.Equals(this.Item1, tuple.Item1)
&& EqualityComparer<T2>.Default.Equals(this.Item2, tuple.Item2);
}
public override int GetHashCode()
{
int h1 = EqualityComparer<T1>.Default.GetHashCode(this.Item1);
int h2 = EqualityComparer<T2>.Default.GetHashCode(this.Item2);
return (h1 << 5) + h1^h2;
}
public bool Equals(object obj, IEqualityComparer comparer)
{
var tuple = obj as Tuple<T1, T2>;
return tuple != null
&& comparer.Equals(this.Item1, tuple.Item1)
&& comparer.Equals(this.Item2, tuple.Item2);
}
public int GetHashCode(IEqualityComparer comparer)
{
int h1 = comparer.GetHashCode(this.Item1);
int h2 = comparer.GetHashCode(this.Item2);
return (h1 << 5) + h1^h2;
}
Ich war überrascht, Gleichheit realisiert auf diese Weise in .NET Tupel-Klasse zu sehen. Ich benutzte den Tupeltyp als Schlüssel in einem der Wörterbücher.
Gibt es einen Grund, warum dies implementiert werden muss, wie im ersten Code gezeigt? Es ist ein wenig entmutigend, in diesem Fall von dieser Klasse Gebrauch zu machen.
Ich glaube nicht, Code-Refactoring und nicht duplizierenden Daten sollten die Hauptsorgen gewesen sein. Die gleiche nicht-generische/boxende Implementierung ist hinter IStructuralComparable
auch gegangen, aber da IStructuralComparable.CompareTo
weniger verwendet wird, ist es kein Problem oft.
gebenchmarkt ich die beiden oben genannten Ansätze mit einem dritten Ansatz, der noch weniger belastend ist, wie dies (nur das Wesentliche):
public override bool Equals(object obj)
{
return this.Equals(obj, EqualityComparer<T1>.Default, EqualityComparer<T2>.Default);
}
public bool Equals(object obj, IEqualityComparer comparer)
{
return this.Equals(obj, comparer, comparer);
}
private bool Equals(object obj, IEqualityComparer comparer1, IEqualityComparer comparer2)
{
var tuple = obj as Tuple<T1, T2>;
return tuple != null
&& comparer1.Equals(this.Item1, tuple.Item1)
&& comparer2.Equals(this.Item2, tuple.Item2);
}
für ein paar Tuple<DateTime, DateTime>
Felder ein 1000000 Equals
Anrufe. Dies ist das Ergebnis:
erster Ansatz (original .NET-Implementierung) - 310 ms
2. Ansatz - 60 ms
3. Ansatz - 130 ms
Die Standardimplementierung ist etwa 4-5 mal langsamer als die optimale Lösung.
Ich sehe keinen Grund, warum 'IEqualityComparer
@MarcinJuraszek Wie ist das besser? 'Tuple <,>' implementiert 'IStructuralEquatable' mit diesen Definitionen' bool Equals (object other, IEqualityComparer-Vergleicher); int GetHashCode (IEqualityComparer-Vergleicher); ' – nawfal
" ... viel benutzte Tuple-Klassen ... ". Da ist dein Problem! – Gusdor