与其描述这两个类的作用,不如描述它对于只读或不可变的实际意味着什么,因为有一个关键区别并没有真正为这两个实现提供太多选择。
只读是类的“接口”的一部分,它的公共方法和属性集。只读意味着该类的外部使用者无法执行任何可能的操作序列来影响其可见状态。例如,与只读文件进行比较;没有应用程序可以使用相同的 API 写入这样的文件,这使得它首先成为只读的成为可能。
只读是否意味着线程安全?不一定——只读类仍然可以使用诸如缓存或优化其内部数据结构之类的东西,并且这些东西可能(很差)以一种在同时调用时会中断的方式实现。
只读是否意味着永远不变?也没有;例如,查看系统时钟。您不能真正影响它(使用默认权限),您只能读取它(根据定义使其为只读),但它的值会根据时间而变化。
永不改变意味着不变。这是一个更强大的概念,并且像线程安全一样,是整个类契约的一部分。类必须主动确保其实例的任何部分在其生命周期内都不会改变,就可以从外部观察到的内容而言。
字符串在 .NET 中是不可变的:只要运行时的完整性没有受到损害(通过内存黑客),字符串的特定实例永远不会与其最初观察到的值不同。另一方面,只读文件并不是不可变的,因为人们总是可以关闭只读并更改文件。
不可变也不意味着线程安全,因为这样的对象仍然可以使用修改其内部状态并且不是线程安全的技术(但通常更容易确保)。
不可变是否意味着只读的问题取决于您如何看待它。您通常可以以不影响可能正在使用它的外部代码的方式“改变”不可变对象,因此公开不可变对象至少与公开只读对象一样强。获取字符串的子字符串就像删除其中的一部分,但以安全的方式。
这让我们回到了关于这两个类的原始问题。ReadOnlyDictionary所要做的就是只读。您仍然必须以某种方式提供数据,使用内部包装的字典,并且只有您自己仍然可以通过内部字典写入数据。包装器提供“强”只读访问(与您通过转换为IReadOnlyDictionary获得的“弱”只读访问相比)。它也是线程安全的,但前提是底层字典也是线程安全的。
ImmutableDictionary可以做更多的事情,因为它拥有的数据不能被更改。本质上,您可以用新数据“修补”其中的一部分并获得结构的修改“副本”,但实际上无需复制完整的对象。由于其实现,它也是线程安全的。与StringBuilder类似,您使用构建器对实例进行更改,然后烘焙它们以生成不可变字典的最终实例。