这是我直到今天才注意到的东西。显然,当执行基于相等的操作时,大量使用的元组类(如Tuple<T>
,Tuple<T1, T2>
等)的.NET实现会导致对值类型的装箱惩罚。.NET Tuple和Equals性能
这里是怎么类框架中的一种实现(通过ILSpy源):
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;
}
}
我看到的问题是它会导致一个两级装箱拆箱,说了Equals
电话,一个,在comparer.Equals
哪个方块的物品,两个,EqualityComparer<object>
调用非通用Equals
,这反过来将在内部不得不将物品解除原始类型。
而是他们为什么不这样做:
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;
}
我很惊讶地看到平等以这种方式实现在.NET中的元组类。我在其中一个字典中使用元组类型作为关键字。
是否有任何理由为什么这必须如第一个代码所示的那样实施?在这种情况下,使用这个类有点令人沮丧。
我不认为代码重构和非重复数据应该是主要关注的问题。同样的非通用/装箱实施也落后于IStructuralComparable
,但由于IStructuralComparable.CompareTo
较少使用,所以它经常不是问题。
我与基准第三种方法是征税还是少,像这样(只要领)上面的两种方法:
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);
}
的一对夫妇Tuple<DateTime, DateTime>
领域一百万Equals
电话。这是结果:
1的方法(原.NET实现) - 310毫秒
第二个方法 - 60毫秒
第三届方法 - 130毫秒
默认实现比最佳解决方案慢大约4-5倍。
我没有看到任何理由为什么'IEqualityComparer
@MarcinJuraszek这样更好吗? '元组<,>'实现'IStructuralEquatable',它具有这些定义'bool Equals(object other,IEqualityComparer comparer); int GetHashCode(IEqualityComparer comparer);' – nawfal
“...多使用元组类...”。有你的问题! – Gusdor