关于.net:C#Dictionary<>

C# Dictionary<> and mutable keys

有人告诉我,在C规范中,使字符串不可变的许多原因之一是为了避免哈希表的问题,即当对字符串键的引用更改了其内容时,键发生了更改。

字典类型允许引用类型用作键。字典如何避免出现导致"错位"值的更改键?当用作密钥时,是否存在由对象构成的memberwise克隆?


Dictionary类型不试图防止用户修改所使用的密钥。完全由开发人员负责不改变密钥。

如果你稍微考虑一下,这真的是唯一明智的方法Dictionary可以服用。考虑在对象上执行类似于memberwise克隆的操作的含义。为了彻底起见,您需要进行深度克隆,因为密钥中引用的对象也可能发生变异,从而影响哈希代码。所以现在表中使用的每个键都克隆了它的完整对象图,以防止发生变异。这将是一个小车,可能是一个非常昂贵的操作。


如果使用可变引用类型作为键,则无论对象状态如何(即哈希绑定到引用,而不是状态),GetHashCode()的默认实现都将确保哈希相等。但是,您是正确的,对于字典键来说,具有值相等语义(gethashcode可能取决于状态)的可变类型是一个错误的选择。


Dictionary<>类不做任何事情来保护自己不受可变密钥对象被更改的影响。这取决于您是否知道作为密钥使用的类是可变的,并尽可能避免它。


如果引用类型不重写equals/gethashcode,则使用默认比较器的字典将不关心任何键对象的字段或属性,因此不会注意或关心它们是否发生更改。最简单的方法是将默认的gethashcode方法看作返回一个与"object id"相关的数字,将默认的equals方法看作比较"object id"的方法。实际上,在限制为20亿或更少对象的系统中,gethashcode可以简单地返回一个对象ID,但出于各种原因,它也可以做其他事情。

如果equals或gethashcode检查对象的唯一部分是对象ID,那么对于这些函数而言,所有对象都是不可变的。一旦创建了一个对象,它将始终具有相同的ID,并且在前一个对象ID的所有痕迹从宇宙中消失之前,该ID将永远不会用于任何其他对象。


这并不能避免这种情况。这取决于调用代码来强制执行:

As long as an object is used as a key in the Dictionary, it must not change in any way that affects its hash value. Every key in a Dictionary must be unique according to the dictionary's equality comparer. A key cannot be null, but a value can be, if the value type TValue is a reference type.

(来自MSDN)