【问题标题】:Why use ImmutableList over ReadOnlyCollection?为什么使用 ImmutableList 而不是 ReadOnlyCollection?
【发布时间】:2015-05-11 10:52:56
【问题描述】:

.NET 4.5 有一个新的命名空间System.Collections.Immutable

这个包提供了线程安全的集合,保证永远不会改变它们的内容,也称为不可变集合。

我很困惑。 ReadOnlyCollection类不是已经解决了线程安全问题吗?为什么要改用ImmutableList


我知道还有一个IReadOnlyList interface。这并不能隐式解决线程安全问题,因为其他线程可能会通过另一个接口编辑对象。

【问题讨论】:

  • 这篇文章似乎解释得很好“blog.slaks.net/2013-06-11/readonly-vs-immutable
  • 仅供参考 System.Collections.Immutable 必须通过 nuget 包管理器安装。我必须在一个针对 .Net 框架 4.6.2 的项目中这样做。

标签: c# .net immutable-collections


【解决方案1】:

ReadOnlyCollection:

只读集合只是一个带有包装器的集合 防止修改集合;因此,如果进行更改 对于基础集合,只读集合反映了那些 变化。

ImmutableList 不会发生这种情况。

【讨论】:

  • 我认为不可变集合最重要的特性是如何通过创建新的不可变集合和底层高效的“写时复制”机制来操作它们。因此,其他两个答案似乎与我更相关。
  • 不变性是一个很大的谎言,至少在 C# .Net 中是这样。我不确定其他编程语言。在 C# 中,即使使用 ImmutableList,任何人都可以通过非托管路由并在 CLR 不知情的情况下更改该内存位置的内容。但是是的,只要所有各方都遵守 .Net 平台提供的合法性,我们可以放心地假设 ImmutableList 确实是不可变的。
  • @RBT 对于任何程序来说都是一样的——如果你有代码在同一个进程中运行,操作系统不会阻止你改变任何东西
  • 即使是不可变的集合也可以通过修改底层集合来修改。我无法理解为什么我们说它做不到?
  • @AshwiniMohan 如果您使用 ImmutableList 的正确公共 API,则没有要修改的基础集合 - 当您调用 Add 等时,它会返回集合的新副本以及添加 -原件保持不变。使用ReadOnlyCollection,虽然您不能通过该类本身修改它,但您仍然可以修改初始化ReadOnlyCollection 的基础集合。如果您开始使用反射或其他修改数据的方法,那么可以 - 根据我之前的评论,一切都会发生。
【解决方案2】:

ReadOnlyCollection<T> 不能解决任何线程安全问题。它只是Ilist<T> 的包装。它不会公开成员来修改集合,但您始终可以使用底层集合引用对其进行修改。

如果基础集合被修改,枚举ReadOnlyCollection<T> 是不安全的。如果这样做,您将获得相同的 InvalidOperationException 消息“集合已修改;枚举操作可能无法执行...”。

来自ReadOnlyCollection<T>

ReadOnlyCollection 可以同时支持多个读取器,如 只要集合没有被修改。即便如此,通过枚举 集合本质上不是线程安全的过程。为保证 枚举期间的线程安全,您可以在枚举期间锁定集合 整个枚举。允许集合被访问 多线程读写,必须自己实现 同步。

另一方面,ImmutableList 是不可变的,因此本质上是线程安全的。

【讨论】:

    【解决方案3】:

    ReadOnlyCollection,顾名思义,只能读取。

    另一方面,您可以通过调用 Add/Remove/Clear 方法向/从 ImmutableList 添加/删除项目,例如,这些方法返回一个新的不可变列表。

    【讨论】:

    • 我要补充一点,这使得ImmutableList<>string 非常相似。
    • @xanatos 确实如此,或任何(正确实现的)值类型。
    • 对于 ImmutableList,与字符串一样,重要的区别并不是您没有修改原始文件。您正在创建一个具有更改的新对象(例如使用 Remove),但原始版本保持不变?
    【解决方案4】:

    在多线程场景中,请注意只读集合仍然不是线程安全的。

    来自ReadOnlyCollection<T> 文档:

    ...如果对基础集合进行了更改,则只读集合会反映这些更改

    由于集合,如 List<T> 和其他集合,不是线程安全的,因此也不是只读集合。

    重要提示:有些极端情况在 MSDN 中没有明确解释。一些看似只读取集合内容的操作,实际上是在修改集合的内部结构。为什么没有指定? - 一个明显的原因是因为这是一个没有反映在 API 上的实现细节。结果就是即使不修改List<T>包装成ReadOnlyCollection<T>,只使用getter,在多线程环境下还是会发生崩溃!

    底线是通用集合,即使包装成ReadOnlyCollection,也不能在开箱即用的多线程环境中使用。

    ReadOnlyCollection 不同,不可变集合确实保证在获得对集合的引用后,任何内部结构都不会改变。请注意,这些结构仍然不是真正不可变的。相反,它们是可冻结的。这意味着该结构将在内部更改一段时间,直到它冻结并返回给调用者。除此之外,对不可变集合的所有其他调用只会使通过原始引用可访问的结构之外的修改。

    结论:只读集合不是线程安全的;不可变集合是线程安全的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-26
      • 2016-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-07
      • 1970-01-01
      相关资源
      最近更新 更多