C#Dictionary 和可变键

有人告诉我,在C#规范中字符串变为不可变的众多原因之一是为了避免在对字符串键的引用改变其内容时HashTables的密钥发生变化的问题。

Dictionary 类型允许将引用类型用作键。 字典如何避免导致“错位”值的更改密钥问题? 当用作键时,是否存在由对象构成的成员克隆?

Dictionary类型不会尝试防止用户修改所使用的密钥。 纯粹由开发人员负责不改变密钥。

如果你想到这一点,这实际上是Dictionary可以采取的唯一理智的路线。 考虑在对象上执行类似成员克隆的操作的含义。 为了彻底,您需要进行深度克隆,因为密钥中引用的对象也可能会发生变异,从而影响哈希代码。 所以现在表中使用的每个键都有克隆的完整对象图,以防止突变。 这将是错误的,可能是非常昂贵的操作。

如果您使用可变引用类型作为键,则GetHashCode()的默认实现将保证哈希相等,而不管对象状态如何(即哈希与引用相关联,而不是与状态相关联)。 但是,你是正确的,具有值相等语义的可变类型(其中GetHashCode可能取决于状态)是字典键的错误选择。

Dictionary<>类没有做任何事情来保护自己免受被更改的可变键对象的影响。 由您来决定您使用的关键类是否可变,并尽可能避免使用它。

它不能避免这种情况。 由调用代码来强制执行此操作:

只要对象在Dictionary用作键,就不能以任何影响其哈希值的方式进行更改。 根据字典的相等比较器Dictionary中的每个键必须是唯一的。 如果值类型TValue是引用类型,则键不能为null ,但值可以是。

(来自MSDN )

如果引用类型不会覆盖Equals / GetHashCode,则使用默认比较器的Dictionary将不关心任何关键对象的字段或属性,因此不会注意或关心它们是否更改。 最简单的方法是将默认的GetHashCode方法视为返回与“对象ID”相关的数字,并将默认的Equals方法视为比较“对象ID”。 实际上,在一个限制为20亿或更少对象的系统中,GetHashCode可以简单地返回一个对象ID,但由于各种原因,它也可以做其他事情。

如果Equals或GetHashCode检查的对象的唯一部分是对象ID,那么为了这些函数的目的,所有对象都是不可变的。 一旦创建了一个对象,它将始终具有相同的ID,并且该ID将永远不会用于任何其他对象,直到前一个对象ID的所有痕迹都从Universe中消失。