ReadOnlyDictionary 和 ImmutableDictionary 之间有什么区别?

What's the difference between a ReadOnlyDictionary and an ImmutableDictionary?

在 C# 中,这两个容器之间的主要区别(在功能或用例方面)是什么? Google.

上似乎没有任何比较这些的信息

System.Collections.ObjectModel.ReadOnlyDictionary System.Collections.Immutable.ImmutableDictionary

我知道 ImmutableDictionary 是线程安全的。一个ReadOnlyDictionary也是这样吗?

这不是 的副本。这个问题是关于如何使用 IReadOnlyDictionary 的。这个问题是关于两者之间的区别(正如有人在 2015 年对该线程的评论,这将是一个不同的问题 - 即这个问题)

  • A ReadOnlyDictionary 可以通过构造函数初始化一次,然后你不能从中添加或删除项目(它们抛出 NotSupportedExceptions)。如果您想确保它在跨应用程序的多个层发送时不会被修改,这将很有用。
  • 一个ImmutableDictionary有方法可以像AddRemove那样修改它,但是他们会创建一个新的字典,return,原来的字典保持不变,新的不可变字典的副本是 returned.

注意:

  • 您通过将另一个字典实例传递给 constructor 来初始化 ReadOnlyDictionary。这解释了为什么 ReadOnlyDictionary 是可变的(如果基础字典被修改)。它只是一个防止直接更改的包装器。
  • 您不能为 ImmutableDictionary 使用构造函数:How can I create a new instance of ImmutableDictionary?

这也解释了为什么 ReadOnlyDictionary 不是线程安全的 (更好:它与底层字典一样线程安全)。 ImmutableDictionary 线程安全的 因为你不能修改原始实例(直接或间接)。所有“修改”它的方法实际上 return 一个新实例。

但是,如果您需要一个线程安全的字典并且它没有必要是不可变的,请改用 ConcurrentDictionary

ReadOnlyDictionary: 是ReadOnly,无法添加或删除

ImmutableDictonary:可以添加或删除,但它像字符串一样是不可变的。有新对象要添加和删除。

A ReadOnlyDictionary<TKey,TValue> 是围绕另一个现有 IDictionary<TKey,TValue> 实现对象的 包装器

重要的是,虽然 "you"(可以访问 ReadOnlyDictionary 的代码)无法通过包装器对字典进行任何更改,但这并不意味着 其他代码 无法修改基础字典。

因此,与其他答案可能暗示的不同,您不能假设 ReadOnlyDictionary 不受修改 - 只是 "you" 不允许修改。因此,例如,您无法确定访问特定密钥的两次尝试会产生相同的结果。

与其描述这两个 class 的作用,还不如描述只读或不可变的实际含义,因为有一个关键的区别,但实际上并没有为这两个实现提供更多选择。

只读是 class 的“接口”的一部分,其 public 方法和属性集。只读意味着 class 的外部消费者无法执行任何可能的操作序列来影响其可见状态。例如与只读文件进行比较;没有应用程序可以使用相同的 API 写入这样的文件,这使得首先可以将其设为只读。

只读是否意味着线程安全?不一定——只读 class 仍然可以使用诸如缓存或优化其内部数据结构之类的东西,并且这些可能(糟糕地)以在并发调用时中断的方式实现。

只读是否意味着永不改变?也没有;例如,查看系统时钟。你不能真正影响它(使用默认权限),你只能读取它(根据定义使其为只读),但它的值会根据时间而变化。

不变就是不变。这是一个更强大的概念,并且像线程安全一样,是整体契约的一部分 class。 class 必须积极确保其实例的任何部分在其生命周期内都不会发生变化,就可以从外部观察到的内容而言。

字符串在 .NET 中是不可变的:只要运行时的完整性不受到损害(通过内存黑客攻击),字符串的特定实例永远不会不同于其最初观察到的值。另一方面,只读文件并不是一成不变的,因为人们总是可以关闭只读并更改文件。

不可变也不意味着线程安全,因为这样的对象仍然可以使用修改其内部状态的技术并且不是线程安全的(但通常更容易确保)。

不可变是否意味着只读的问题取决于你如何看待它。您通常可以以不影响可能正在使用它的外部代码的方式“改变”一个不可变对象,因此暴露一个不可变对象至少与暴露一个只读对象一样强大。取一个字符串的子串就像删除它的一部分,但是是以一种安全的方式。


这让我们回到最初关于两个 class 的问题。 ReadOnlyDictionary 所要做的就是只读。您仍然必须以某种方式提供数据,使用内部包装的字典,并且只有您仍然可以通过内部字典写入数据。包装器提供“强”只读访问(与仅通过强制转换为 IReadOnlyDictionary 获得的“弱”只读访问相比)。它也是线程安全的,但前提是底层字典也是线程安全的。

ImmutableDictionary 可以做更多的事情,因为它拥有的数据不能被更改。从本质上讲,您可以用新数据“修补”它的一部分并获得结构的修改“副本”,但实际上不需要复制完整的对象。由于其实现,它也是线程安全的。与 StringBuilder 类似,您可以使用构建器对实例进行更改,然后烘焙它们以生成不可变字典的最终实例。