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