为什么 KeyValuePair 不覆盖 Equals() 和 GetHashCode()?
Why does KeyValuePair not override Equals() and GetHashCode()?
我打算在比较密集型代码中使用 KeyValuePair
,但在检查它是如何在 .NET 中实现时感到困惑(见下文)
为什么它不覆盖 Equals
和 GetHashCode
以提高效率(而不是实现 ==
)而是使用基于慢速反射的默认实现?
我知道 structs/value 类型的 GetHashCode()
和 Equals(object)
方法有一个基于反射的默认实现,但我想如果你这样做的话,与覆盖相等性相比,它是非常低效的很多比较。
EDIT 我做了一些测试,发现在我的场景(WPF 列表)中,默认 KeyValuePair
和我自己实现的结构覆盖 GetHashCode()
和 Equals(object)
都比 class!
的实现慢得多
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Interface: KeyValuePair
**
** <OWNER>[....]</OWNER>
**
**
** Purpose: Generic key-value pair for dictionary enumerators.
**
**
===========================================================*/
namespace System.Collections.Generic {
using System;
using System.Text;
// A KeyValuePair holds a key and a value from a dictionary.
// It is used by the IEnumerable<T> implementation for both IDictionary<TKey, TValue>
// and IReadOnlyDictionary<TKey, TValue>.
[Serializable]
public struct KeyValuePair<TKey, TValue> {
private TKey key;
private TValue value;
public KeyValuePair(TKey key, TValue value) {
this.key = key;
this.value = value;
}
public TKey Key {
get { return key; }
}
public TValue Value {
get { return value; }
}
public override string ToString() {
StringBuilder s = StringBuilderCache.Acquire();
s.Append('[');
if( Key != null) {
s.Append(Key.ToString());
}
s.Append(", ");
if( Value != null) {
s.Append(Value.ToString());
}
s.Append(']');
return StringBuilderCache.GetStringAndRelease(s);
}
}
}
KeyValuePair
是一个结构(隐式继承自 ValueType
)并且相等性工作得很好:
var a = new KeyValuePair<string, string>("a", "b");
var b = new KeyValuePair<string, string>("a", "b");
bool areEqual = a.Equals(b); // true
下面的参考显示了等于策略:
1- 相同的引用。
2- 可以按位比较。
3- 使用反射比较结构中的每个字段。
public abstract class ValueType {
[System.Security.SecuritySafeCritical]
public override bool Equals (Object obj) {
BCLDebug.Perf(false, "ValueType::Equals is not fast. "+this.GetType().FullName+" should override Equals(Object)");
if (null==obj) {
return false;
}
RuntimeType thisType = (RuntimeType)this.GetType();
RuntimeType thatType = (RuntimeType)obj.GetType();
if (thatType!=thisType) {
return false;
}
Object thisObj = (Object)this;
Object thisResult, thatResult;
// if there are no GC references in this object we can avoid reflection
// and do a fast memcmp
if (CanCompareBits(this))
return FastEqualsCheck(thisObj, obj);
FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int i=0; i<thisFields.Length; i++) {
thisResult = ((RtFieldInfo)thisFields[i]).UnsafeGetValue(thisObj);
thatResult = ((RtFieldInfo)thisFields[i]).UnsafeGetValue(obj);
if (thisResult == null) {
if (thatResult != null)
return false;
}
else
if (!thisResult.Equals(thatResult)) {
return false;
}
}
return true;
}
它是一个结构体,Structs 继承自 ValueType
并且类型 already overrides the implementation of Equals and GetHashCode。
不支持==
,下面的操作连编译都不会
var result = new KeyValuePair<string, string>("KVP", "Test1") ==
new KeyValuePair<string, string>("KVP", "Test2");
您将收到错误 "Operator '==' cannot be applied to operands of type KeyValuePair<string, string>
and KeyValuePair<string, string>
"
正如其他答案所指出的那样,您获得了平等和散列 "for free",因此您不需要覆盖它们。但是,一分钱一分货;相等和散列的默认实现是 (1) 在某些情况下不是特别有效,并且 (2) 可以进行按位比较,因此可以做一些事情,比如比较负零和正零双打在逻辑上是不同的。
如果您预计您的结构将经常用于需要相等性和散列的上下文中,那么您应该编写两者的自定义实现并遵循适当的规则和准则。
https://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/
所以,回答你的问题:为什么没有人为特定类型这样做?可能是因为他们认为与改进基础 class 库所必须做的所有其他事情相比,这样做是对时间的一种很好的利用。大多数人不会比较键值对是否相等,因此优化它可能不是一个高优先级。
这当然是推测;如果你真的想知道某件事 没有 在某一天完成的原因,你将不得不追踪 所有 没有做那个动作的人问他们那天他们还做了什么更重要的事情。
我打算在比较密集型代码中使用 KeyValuePair
,但在检查它是如何在 .NET 中实现时感到困惑(见下文)
为什么它不覆盖 Equals
和 GetHashCode
以提高效率(而不是实现 ==
)而是使用基于慢速反射的默认实现?
我知道 structs/value 类型的 GetHashCode()
和 Equals(object)
方法有一个基于反射的默认实现,但我想如果你这样做的话,与覆盖相等性相比,它是非常低效的很多比较。
EDIT 我做了一些测试,发现在我的场景(WPF 列表)中,默认 KeyValuePair
和我自己实现的结构覆盖 GetHashCode()
和 Equals(object)
都比 class!
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Interface: KeyValuePair
**
** <OWNER>[....]</OWNER>
**
**
** Purpose: Generic key-value pair for dictionary enumerators.
**
**
===========================================================*/
namespace System.Collections.Generic {
using System;
using System.Text;
// A KeyValuePair holds a key and a value from a dictionary.
// It is used by the IEnumerable<T> implementation for both IDictionary<TKey, TValue>
// and IReadOnlyDictionary<TKey, TValue>.
[Serializable]
public struct KeyValuePair<TKey, TValue> {
private TKey key;
private TValue value;
public KeyValuePair(TKey key, TValue value) {
this.key = key;
this.value = value;
}
public TKey Key {
get { return key; }
}
public TValue Value {
get { return value; }
}
public override string ToString() {
StringBuilder s = StringBuilderCache.Acquire();
s.Append('[');
if( Key != null) {
s.Append(Key.ToString());
}
s.Append(", ");
if( Value != null) {
s.Append(Value.ToString());
}
s.Append(']');
return StringBuilderCache.GetStringAndRelease(s);
}
}
}
KeyValuePair
是一个结构(隐式继承自 ValueType
)并且相等性工作得很好:
var a = new KeyValuePair<string, string>("a", "b");
var b = new KeyValuePair<string, string>("a", "b");
bool areEqual = a.Equals(b); // true
下面的参考显示了等于策略:
1- 相同的引用。
2- 可以按位比较。
3- 使用反射比较结构中的每个字段。
public abstract class ValueType {
[System.Security.SecuritySafeCritical]
public override bool Equals (Object obj) {
BCLDebug.Perf(false, "ValueType::Equals is not fast. "+this.GetType().FullName+" should override Equals(Object)");
if (null==obj) {
return false;
}
RuntimeType thisType = (RuntimeType)this.GetType();
RuntimeType thatType = (RuntimeType)obj.GetType();
if (thatType!=thisType) {
return false;
}
Object thisObj = (Object)this;
Object thisResult, thatResult;
// if there are no GC references in this object we can avoid reflection
// and do a fast memcmp
if (CanCompareBits(this))
return FastEqualsCheck(thisObj, obj);
FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int i=0; i<thisFields.Length; i++) {
thisResult = ((RtFieldInfo)thisFields[i]).UnsafeGetValue(thisObj);
thatResult = ((RtFieldInfo)thisFields[i]).UnsafeGetValue(obj);
if (thisResult == null) {
if (thatResult != null)
return false;
}
else
if (!thisResult.Equals(thatResult)) {
return false;
}
}
return true;
}
它是一个结构体,Structs 继承自 ValueType
并且类型 already overrides the implementation of Equals and GetHashCode。
不支持==
,下面的操作连编译都不会
var result = new KeyValuePair<string, string>("KVP", "Test1") ==
new KeyValuePair<string, string>("KVP", "Test2");
您将收到错误 "Operator '==' cannot be applied to operands of type KeyValuePair<string, string>
and KeyValuePair<string, string>
"
正如其他答案所指出的那样,您获得了平等和散列 "for free",因此您不需要覆盖它们。但是,一分钱一分货;相等和散列的默认实现是 (1) 在某些情况下不是特别有效,并且 (2) 可以进行按位比较,因此可以做一些事情,比如比较负零和正零双打在逻辑上是不同的。
如果您预计您的结构将经常用于需要相等性和散列的上下文中,那么您应该编写两者的自定义实现并遵循适当的规则和准则。
https://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/
所以,回答你的问题:为什么没有人为特定类型这样做?可能是因为他们认为与改进基础 class 库所必须做的所有其他事情相比,这样做是对时间的一种很好的利用。大多数人不会比较键值对是否相等,因此优化它可能不是一个高优先级。
这当然是推测;如果你真的想知道某件事 没有 在某一天完成的原因,你将不得不追踪 所有 没有做那个动作的人问他们那天他们还做了什么更重要的事情。