以只读形式返回集合
我在多线程环境中有一个对象,该对象维护着信息的集合,例如:
public IList<string> Data { get { return data; } }
我目前有"返回数据",由" ReaderWriterLockSlim"包装,以保护集合免于共享违规。但是,可以肯定的是,我想将集合返回为只读,以便调用代码无法更改集合,而仅查看已经存在的内容。这是可能吗?
解决方案
回答
如果基础数据存储为列表,则可以使用List(T).AsReadOnly方法。
如果可以枚举数据,则可以使用Enumerable.ToList方法将集合转换为List并在其上调用AsReadOnly。
回答
应该注意的是,aku的答案只会保护列表为只读。列表中的元素仍然非常可写。我不知道是否有任何方法可以在将非原子元素放入只读列表之前对其进行克隆而不对其进行克隆。
回答
如果唯一目的是使调用代码不会出错,并且在仅应读取其内容时修改集合,则只需返回不支持Add,Remove等的接口即可。为什么不返回IEnumerable <string>
?调用代码将不得不进行强制转换,而在不知道所访问属性的内部的情况下,他们不太可能这样做。
但是,如果目的是防止调用代码观察其他线程的更新,则我们必须退回到已经提到的解决方案,根据需要执行深层或者浅层副本。
回答
我认为我们在这里混淆了概念。
" ReadOnlyCollection"为现有集合提供了一个只读包装器,允许我们(A类)在调用者(B类)无法修改集合(即无法添加或者删除)的情况下传递对集合安全的引用。集合中的任何元素。)
绝对没有线程安全保证。
- 如果我们(A类)在将其作为" ReadOnlyCollection"分发后继续修改基础集合,则B类将看到这些更改,使任何迭代器无效等,并且通常对集合的任何常见并发问题持开放态度。
- 此外,如果集合中的元素是可变的,那么我们(类A)和调用者(类B)都将能够更改集合中对象的任何可变状态。
实现取决于需求:
如果我们不关心呼叫者(B类)是否对集合有任何进一步的更改,则可以仅克隆集合,将其分发出去并停止维护。
如果我们确实需要调用方(B类)查看对该集合所做的更改,并且希望此操作是线程安全的,那么我们手头上还有更多问题。一种可能是实现我们自己的ReadOnlyCollection的线程安全变体以允许锁定访问,但是如果我们要支持IEnumerable,这将是不平凡且不可靠的,并且仍然无法保护我们免受IEnumerable中可变元素的影响。收藏。
回答
我们可以使用集合的副本。
public IList<string> Data { get { return new List<T>(data); }}
这样一来,是否更新就无关紧要。
回答
我们要使用yield关键字。我们遍历IEnumerable列表,并使用yeild返回结果。这样,消费者就可以在不修改集合的情况下将它们用于。
它看起来像这样:
List<string> _Data; public IEnumerable<string> Data { get { foreach(string item in _Data) { return yield item; } } }
回答
我对我们接受的答案投了赞成票,并同意它-但是我可以给我们一些考虑的方法吗?
不要直接返回集合。创建一个准确命名的业务逻辑类,以反映集合的目的。
这样做的主要优势在于我们无法向集合中添加代码,因此,只要我们在对象模型中具有本机"集合",就始终在整个项目中散布非OO支持代码来访问它。
例如,如果托收是发票,则代码中可能有3或者4个地方可以遍历未付款的发票。我们可以使用getUnpaidInvoices方法。但是,当我们开始考虑诸如" payUnpaidInvoices(payer,account);"之类的方法时,真正的力量就来了。
当我们传递集合而不是编写对象模型时,我们将永远不会经历整个重构类。
还请注意,这使问题特别好。如果我们不希望人们更改收藏夹,则容器不需要包含任何变种器。如果以后再决定仅在一种情况下实际上必须进行修改,则可以创建一个安全的机制来进行修改。
当我们传递本机集合时,如何解决该问题?
而且,本机集合无法使用额外的数据进行增强。下次我们发现将(Collection,Extra)传递给一种或者两种以上方法时,我们会认识到这一点。它表示"额外"属于包含集合的对象。