将对象添加到.NET System.Collections.Generic.Dictionary类时,密钥的哈希码将在内部存储并用于以后的比较 . 当哈希码在其初始插入字典后发生变化时,它常常变为"inaccessible",并且当存在检查(即使使用相同的引用)返回false时,可能会使其用户感到惊讶(下面的示例代码) .
GetHashCode文档说:
对象的GetHashCode方法必须始终返回相同的哈希码,只要对对象状态没有修改即可确定对象的Equals方法的返回值 .
因此,根据 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);
}
}
2 回答
不,你不应该在将它插入字典后改变键(以物质方式) . 这是设计,以及我曾经使用的每个哈希表的工作方式 . 文档甚至指定了这个:
所以它只会给那些不阅读文档的用户带来惊喜:)
要添加Jon的答案,我只想强调你引用的文档的某个部分:
现在,就在那里,你违反了规则 . 您更改了
PK
,这不会影响Equals
的结果(因为您在那里检查了ReferenceEquals
),但GetHashCode
的结果确实发生了变化 . 这就是简单的答案 .采用更概念化的方法,我认为你可以这样看:如果你已经覆盖了你的类型的
Equals
和GetHashCode
行为,那么你已经拥有了对于这种类型的一个实例意味着什么的概念 . 等于另一个 . 事实上,你已经以一种Hashable
对象可以变成完全不同的方式来定义它;即,某些东西不再像以前那样可用(因为它的哈希码已经改变) .从这个角度考虑,在你执行
dict.Add(h, true)
,然后你改变h.PK
之后,字典 doesn't 再包含h
引用的对象 . 它包含了其他的东西(它没有像你定义的类型那样是一条脱掉它皮肤的蛇 .