【发布时间】:2011-03-01 17:46:57
【问题描述】:
有人告诉我,在 C# 规范中将字符串设为不可变的众多原因之一是为了避免 HashTable 在对字符串键的引用更改其内容时更改键的问题。
Dictionary 类型允许将引用类型用作键。字典如何避免导致“错位”值的更改键的问题?当用作键时,是否存在由对象组成的成员克隆?
【问题讨论】:
标签: c# .net dictionary hashtable
有人告诉我,在 C# 规范中将字符串设为不可变的众多原因之一是为了避免 HashTable 在对字符串键的引用更改其内容时更改键的问题。
Dictionary 类型允许将引用类型用作键。字典如何避免导致“错位”值的更改键的问题?当用作键时,是否存在由对象组成的成员克隆?
【问题讨论】:
标签: c# .net dictionary hashtable
如果引用类型不覆盖 Equals/GetHashCode,则使用默认比较器的字典不会关心任何关键对象的字段或属性,因此不会注意到或关心它们是否发生变化。最简单的方法是将默认的 GetHashCode 方法视为返回与“对象 ID”相关的数字,将默认的 Equals 方法视为比较“对象 ID”。实际上,在一个对象限制为 20 亿或更少的系统中,GetHashCode 可以简单地返回一个对象 ID,但由于各种原因,它也可能做其他事情。
如果由 Equals 或 GetHashCode 检查的对象的唯一部分是对象 ID,那么就这些函数而言,所有对象都是不可变的。一旦创建了一个对象,它将始终具有相同的 ID,并且在前一个对象 ID 的所有痕迹从 Universe 中消失之前,该 ID 将永远不会用于任何其他对象。
【讨论】:
如果您使用可变引用类型作为键,GetHashCode() 的默认实现将保证哈希相等,而不管对象状态如何(即哈希与引用相关联,而不是状态)。但是,您是对的,具有值相等语义(其中 GetHashCode 可能取决于状态)的可变类型对于字典键来说是一个糟糕的选择。
【讨论】:
double、Decimal、List<T>.Enumerator 等),但.net 中的大多数类型都实现了Equals(Object) 以表示等价;由于不同的可变类类型项永远不会等价,因此它们永远不应将自己报告为Equals(List<T>.Enumerator 的行为源于装箱和未装箱的值类型具有不同的语义,但需要共享相同的 @987654328 @方法)。
它并没有避免这种情况。由调用代码来强制执行此操作:
只要将对象用作
Dictionary<TKey, TValue>中的键,它就不能以任何影响其哈希值的方式发生变化。根据字典的相等比较器,Dictionary<TKey, TValue>中的每个键都必须是唯一的。键不能是null,但值可以是,如果值类型TValue是引用类型。
(来自MSDN)
【讨论】:
Dictionary<TKey,TValue> 类型不会尝试防止用户修改所使用的密钥。完全由开发人员负责不改变密钥。
如果您仔细考虑一下,这确实是唯一合理的方法
Dictionary<TKey,TValue>可以拿。考虑在对象上执行类似成员克隆的操作的含义。为了彻底,您需要进行深度克隆,因为键中引用的对象也可能发生变异,从而影响哈希码。因此,现在表中使用的每个键都克隆了完整的对象图,以防止突变。这将是错误的,并且可能是一项非常昂贵的操作。
【讨论】:
Dictionary<> 类不会保护自己免受可变键对象的更改。由您决定您用作键的类是否是可变的,并尽可能避免它。
【讨论】: