我在多线程环境中有一个对象,它维护信息集合,例如:

public IList<string> Data 
{
    get 
    {
        return data;
    }
}

我目前有 return data; 包裹着一个 ReaderWriterLockSlim 保护集合免受共享违规行为的影响。但是,为了更加确定,我想将集合返回为只读,以便调用代码无法对集合进行更改,只能查看已有的内容。这是可能吗?

有帮助吗?

解决方案

如果您的唯一目的是让调用代码不犯错误,并在只应读取集合时修改集合,那么所需要做的就是返回一个不支持添加、删除等的接口。为什么不回来 IEnumerable<string>?调用代码必须进行强制转换,如果不知道他们正在访问的属性的内部结构,他们不太可能这样做。

但是,如果您的目的是阻止调用代码观察其他线程的更新,您将不得不退回到已经提到的解决方案,根据您的需要执行深复制或浅复制。

其他提示

如果您的基础数据存储为列表,您可以使用 列表(T).AsReadOnly 方法。
如果您的数据可以枚举,您可以使用 可枚举.ToList 方法将集合转换为 List 并对其调用 AsReadOnly 。

我投票支持你接受的答案并同意它——但是我可以给你一些考虑吗?

不要直接返回集合。创建一个准确命名的业务逻辑类,以反映集合的目的。

这样做的主要优点在于,您无法将代码添加到集合中,因此每当您的对象模型中有本机“集合”时,您总是可以在整个项目中传播非 OO 支持代码来访问它。

例如,如果您的集合是发票,则您的代码中可能有 3 或 4 个位置迭代未付发票。您可以有一个 getUnpaidInvoices 方法。然而,当您开始考虑像“payUnpaidInvoices(payer, account);”这样的方法时,真正的力量就出现了。

当您传递集合而不是编写对象模型时,您将永远不会想到重构整个类。

另请注意,这使您的问题变得特别好。如果您不希望人们更改集合,则您的容器不需要包含任何修改器。如果您后来决定只有在一种情况下您实际上必须修改它,您可以创建一个安全机制来执行此操作。

当你传递原生集合时,你如何解决这个问题?

此外,无法使用额外数据来增强本机集合。下次当您发现将 (Collection, Extra) 传递给不止一两个方法时,您就会认识到这一点。它表明“Extra”属于包含您的集合的对象。

我认为你在这里混淆了概念。

ReadOnlyCollection 为现有集合提供只读包装器,允许您(A 类)在知道调用者(B 类)无法修改集合的情况下安全地传递对集合的引用(即。不能 添加 或者 消除 集合中的任何元素。)

绝对没有线程安全保证。

  • 如果您(A 类)在将底层集合作为 ReadOnlyCollection 那么 B 类将看到这些更改,使任何迭代器失效,等等。并且通常对集合的任何常见并发问题持开放态度。
  • 此外,如果集合中的元素是可变的,那么您(A 类) 调用者(B 类)将能够更改集合中对象的任何可变状态。

您的实施取决于您的需求:- 如果您不关心调用者(B 类)看到集合的任何进一步更改,那么您可以克隆集合,将其分发出去,然后停止关心。- 如果您确实需要调用者(B 类)查看对集合所做的更改,并且您希望它是线程安全的,那么您遇到的问题就更多了。一种可能性是实现您自己的 ReadOnlyCollection 的线程安全变体以允许锁定访问,尽管如果您想支持 IEnumerable,这将是不平凡且不高性能的,并且它 仍然 不会保护您免受集合中可变元素的影响。

应该注意的是 阿库的答案只会将列表保护为只读。列表中的元素仍然非常可写。我不知道是否有任何方法可以保护非原子元素,而无需在将它们放入只读列表之前对其进行克隆。

您想使用 屈服 关键词。您循环遍历 IEnumerable 列表并返回带有 yeild 的结果。这允许消费者在不修改集合的情况下使用 foreach。

它看起来像这样:

List<string> _Data;
public IEnumerable<string> Data
{
  get
  {
    foreach(string item in _Data)
    {
      return yield item;
    }
  }
}

您可以使用该集合的副本。

public IList<string> Data {
get {
    return new List<T>(data);
}}

这样就算更新了也没关系。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top