【发布时间】:2011-02-02 07:35:06
【问题描述】:
将对象添加到 .NET System.Collections.Generic.Dictionary 类时,键的哈希码会在内部存储并用于以后的比较。当哈希码在其初始插入字典后发生更改时,它通常会变得“不可访问”,并且当存在性检查(即使使用相同的引用)返回 false 时,它的用户可能会感到惊讶(下面的示例代码)。
GetHashCode 文档说:
只要没有对确定对象的 Equals 方法返回值的对象状态进行修改,对象的 GetHashCode 方法就必须始终返回相同的哈希码。
因此,根据GetHashCode 文档,每当equality-determining 状态更改时,哈希码可能会更改,但Dictionary 实现不支持这一点。
当前的 .NET 字典实现是否因为它错误地忽略了哈希码允许而被破坏? GetHashCode() 应该只基于不可变成员吗?或者,是否还有其他东西可以打破可能的错误二分法?
class Hashable
{
public int PK { get; set; }
public override int GetHashCode()
{
if (PK != 0) return PK.GetHashCode();
return base.GetHashCode();
}
public override bool Equals(object obj)
{
return Equals(obj as Hashable);
}
public virtual bool Equals(Hashable other)
{
if (other == null) return false;
else if (ReferenceEquals(this, other)) return true;
else if (PK != 0 && other.PK != 0) return Equals(PK, other.PK);
return false;
}
public override string ToString()
{
return string.Format("Hashable {0}", PK);
}
}
class Test
{
static void Main(string[] args)
{
var dict = new Dictionary<Hashable, bool>();
var h = new Hashable();
dict.Add(h, true);
h.PK = 42;
if (!dict.ContainsKey(h)) // returns false, despite same reference
dict.Add(h, false);
}
}
【问题讨论】:
-
如果字典支持改变键,它必须挂接到键中,并在 GetHashCode() 更改时以某种方式得到通知。这样做的意义听起来很大,可能简直是不可能的。
-
我一直认为字典是基于诸如底层对象 id 或指针之类的东西(我还不确定 .NET 有),但你是对的 - HashCode 是一个完美的实现选择一本字典。
-
其实这个设计是为了允许你使用更复杂的类型作为key;你只需要正确设计它们。 (例如,如果它使用对象指针,则由于将复杂类型的两个实例作为键,您最终可能会在字典中出现重复项,即使您正确设计了该复杂类型来表示,例如复合键)。
标签: c# .net dictionary immutability hashcode