C# 以只读方式返回集合

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/55502/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 10:09:30  来源:igfitidea点击:

Return collection as read-only

提问by alastairs

I have an object in a multi-threaded environment that maintains a collection of information, e.g.:

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

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

I currently have return data;wrapped by a ReaderWriterLockSlimto protect the collection from sharing violations. However, to be doubly sure, I'd like to return the collection as read-only, so that the calling code is unable to make changes to the collection, only view what's already there. Is this at all possible?

我目前return data;由 a 包装,ReaderWriterLockSlim以保护集合免受共享违规。但是,要加倍确定,我想将集合作为只读返回,以便调用代码无法对集合进行更改,只能查看已经存在的内容。这是可能吗?

采纳答案by aku

If your underlying data is stored as list you can use List(T).AsReadOnlymethod.
If your data can be enumerated, you can use Enumerable.ToListmethod to cast your collection to List and call AsReadOnly on it.

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

回答by Kris Erickson

One should note that aku's answer will only protect the list as being read only. Elements in the list are still very writable. I don't know if there is any way of protecting non-atomic elements without cloning them before placing them in the read only list.

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

回答by nedruod

If your only intent is to get calling code to not make a mistake, and modify the collection when it should only be reading all that is necessary is to return an interface which doesn't support Add, Remove, etc.. Why not return IEnumerable<string>? Calling code would have to cast, which they are unlikely to do without knowing the internals of the property they are accessing.

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

If however your intent is to prevent the calling code from observing updates from other threads you'll have to fall back to solutions already mentioned, to perform a deep or shallow copy depending on your need.

但是,如果您的意图是防止调用代码观察来自其他线程的更新,您将不得不退回到已经提到的解决方案,根据您的需要执行深拷贝或浅拷贝。

回答by Daniel Fortunov

I think you're confusing concepts here.

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

The ReadOnlyCollectionprovides a read-only wrapper for an existing collection, allowing you (Class A) to pass out a reference to the collection safe in the knowledge that the caller (Class B) cannot modify the collection (i.e. cannot addor removeany elements from the collection.)

ReadOnlyCollection提供了一个只读一个现有的集合包装,让您(A类)传递出的收集安全基准的知识呼叫者(B类),不能修改该集合(即不能添加删除任何元素集合。)

There are absolutely no thread-safety guarantees.

绝对没有线程安全保证。

  • If you (Class A) continue to modify the underlying collection after you hand it out as a ReadOnlyCollectionthen class B will see these changes, have any iterators invalidated, etc. and generally be open to any of the usual concurrency issues with collections.
  • Additionally, if the elements within the collection are mutable, both you (Class A) andthe caller (Class B) will be able to change any mutable state of the objects within the collection.
  • 如果您(A 类)在将其作为 a 分发后继续修改底层集合,ReadOnlyCollection则 B 类将看到这些更改,使任何迭代器失效等,并且通常对集合的任何常见并发问题持开放态度。
  • 此外,如果集合中的元素是可变的,则您(A 类)调用者(B 类)都可以更改集合中对象的任何可变状态。

Your implementation depends on your needs: - If you don't care about the caller (Class B) from seeing any further changes to the collection then you can just clone the collection, hand it out, and stop caring. - If you definitely need the caller (Class B) to see changes that are made to the collection, and you want this to be thread-safe, then you have more of a problem on your hands. One possibility is to implement your own thread-safe variant of the ReadOnlyCollection to allow locked access, though this will be non-trivial and non-performant if you want to support IEnumerable, and it stillwon't protect you against mutable elements in the collection.

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

回答by Dror Helper

You can use a copy of the collection instead.

您可以改用集合的副本。

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

That way it doesn't matter if it gets updated.

这样它是否被更新并不重要。

回答by Charles Graham

You want to use the yieldkeyword. You loop through the IEnumerable list and return the results with yeild. This allows the consumer to use the for each without modifying the collection.

您想使用yield关键字。您遍历 IEnumerable 列表并使用 yeild 返回结果。这允许使用者在不修改集合的情况下使用 for each。

It would look something like this:

它看起来像这样:

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

回答by Bill K

I voted for your accepted answer and agree with it--however might I give you something to consider?

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

Don't return a collection directly. Make an accurately named business logic class that reflects the purpose of the collection.

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

The main advantage of this comes in the fact that you can't add code to collections so whenever you have a native "collection" in your object model, you ALWAYS have non-OO support code spread throughout your project to access it.

这样做的主要优点在于,您无法向集合添加代码,因此只要您的对象模型中有本机“集合”,您总是会在整个项目中使用非 OO 支持代码来访问它。

For instance, if your collection was invoices, you'd probably have 3 or 4 places in your code where you iterated over unpaid invoices. You could have a getUnpaidInvoices method. However, the real power comes in when you start to think of methods like "payUnpaidInvoices(payer, account);".

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

When you pass around collections instead of writing an object model, entire classes of refactorings will never occur to you.

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

Note also that this makes your problem particularly nice. If you don't want people changing the collections, your container need contain no mutators. If you decide later that in just one case you actually HAVE to modify it, you can create a safe mechanism to do so.

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

How do you solve that problem when you are passing around a native collection?

当您传递本地集合时,您如何解决该问题?

Also, native collections can't be enhanced with extra data. You'll recognize this next time you find that you pass in (Collection, Extra) to more than one or two methods. It indicates that "Extra" belongs with the object containing your collection.

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