浏览一下string.GetHashCode
使用Reflector的源代码会发现以下内容(对于 mscorlib.dll 版本 4.0):
public override unsafe int GetHashCode()
{
fixed (char* str = ((char*) this))
{
char* chPtr = str;
int num = 0x15051505;
int num2 = num;
int* numPtr = (int*) chPtr;
for (int i = this.Length; i > 0; i -= 4)
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
if (i <= 2)
{
break;
}
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
numPtr += 2;
}
return (num + (num2 * 0x5d588b65));
}
}
现在,我意识到的实现GetHashCode
没有指定并且是依赖于实现的,所以问题是“GetHashCode
以 X 或 Y 的形式实现?” 不是真的可以回答。我只是对几件事感到好奇:
- 如果 Reflector 正确地反汇编了 DLL 并且这是(在我的环境中)的实现
GetHashCode
,我是否正确地解释了这段代码以指示string
基于这个特定实现的对象不会缓存其哈希码? - 假设答案是肯定的,为什么会这样?在我看来,内存成本将是最小的(多一个 32 位整数,与字符串本身的大小相比减少了),而节省的成本将是显着的,尤其是在使用字符串的情况下作为基于哈希表的集合中的键,例如
Dictionary<string, [...]>
. 而且由于string
该类是不可变的,因此返回的值GetHashCode
甚至不会改变。
我会错过什么?
更新:回应安德拉斯佐尔坦的闭幕词:
蒂姆的回答(+1 那里)也很重要。如果他是对的,我认为他是对的,那么就不能保证字符串在构造后实际上是不可变的,因此缓存结果是错误的。
哇哇哇!_ 这是一个有趣的观点(是的,这是非常正确的),但我真的怀疑在GetHashCode
. “因此缓存结果是错误的”这句话对我来说意味着框架对字符串的态度是“好吧,它们应该是不可变的,但如果开发人员想要偷偷摸摸,它们是可变的,所以我们会处理他们就是这样。” 这绝对不是框架查看字符串的方式。它在很多方面完全依赖于它们的不变性(字符串文字的实习,将所有零长度字符串分配给string.Empty
等),基本上,如果你改变一个字符串,你正在编写其行为完全未定义和不可预测的代码。
我想我的意思是让这个实现的作者担心,“如果这个字符串实例在调用之间被修改了怎么办,即使它公开暴露的类是不可变的?” 就像一个计划休闲户外烧烤的人会想他/她自己,“如果有人把原子弹带到聚会上怎么办?” 听着,如果有人带了原子弹,派对就结束了。