C# 如何公开集合属性?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35007/
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
How to expose a collection property?
提问by jedatu
Every time I create an object that has a collection property I go back and forth on the best way to do it?
每次我创建一个具有集合属性的对象时,我都会以最佳方式来回执行吗?
- public property with a getter that returns a reference to private variable
- explicit get_ObjList and set_ObjList methods that return and create new or cloned objects every time
- explicit get_ObjList that returns an IEnumerator and a set_ObjList that takes IEnumerator
- 带有返回私有变量引用的 getter 的公共属性
- 每次都返回和创建新对象或克隆对象的显式 get_ObjList 和 set_ObjList 方法
- 返回 IEnumerator 的显式 get_ObjList 和采用 IEnumerator 的 set_ObjList
Does it make a difference if the collection is an array (i.e., objList.Clone()) versus a List?
如果集合是一个数组(即 objList.Clone())与一个列表,它会有所不同吗?
If returning the actual collection as a reference is so bad because it creates dependencies, then why return any property as a reference? Anytime you expose an child object as a reference the internals of that child can be changed without the parent "knowing" unless the child has a property changed event. Is there a risk for memory leaks?
如果将实际集合作为引用返回是如此糟糕,因为它会创建依赖关系,那么为什么要返回任何属性作为引用呢?任何时候您将子对象作为引用公开时,除非子对象具有属性更改事件,否则可以在父对象“不知道”的情况下更改该子对象的内部结构。是否存在内存泄漏风险?
And, don't options 2 and 3 break serialization? Is this a catch 22 or do you have to implement custom serialization anytime you have a collection property?
而且,选项 2 和 3 不会破坏序列化吗?这是一个捕获 22 还是您必须在拥有集合属性的任何时候实现自定义序列化?
The generic ReadOnlyCollection seems like a nice compromise for general use. It wraps an IList and restricts access to it. Maybe this helps with memory leaks and serialization. However it still has enumeration concerns
通用 ReadOnlyCollection 似乎是一般用途的一个很好的折衷方案。它包装一个 IList 并限制对它的访问。也许这有助于内存泄漏和序列化。但是它仍然存在枚举问题
Maybe it just depends. If you don't care that the collection is modified, then just expose it as a public accessor over a private variable per #1. If you don't want other programs to modify the collection then #2 and/or #3 is better.
也许这只是取决于。如果您不关心该集合是否被修改,则只需将其公开为每个 #1 的私有变量上的公共访问器。如果您不希望其他程序修改集合,那么 #2 和/或 #3 会更好。
Implicit in the question is why should one method be used over another and what are the ramifications on security, memory, serialization, etc.?
问题中隐含的是为什么应该使用一种方法而不是另一种方法,以及对安全性、内存、序列化等的影响是什么?
采纳答案by Emperor XLII
How you expose a collection depends entirely on how users are intended to interact with it.
如何公开集合完全取决于用户打算如何与之交互。
1)If users will be adding and removing items from an object's collection, then a simple get-only collection property is best (option #1 from the original question):
1)如果用户要从对象的集合中添加和删除项目,那么最好使用简单的仅获取集合属性(原始问题中的选项 #1):
private readonly Collection<T> myCollection_ = new ...;
public Collection<T> MyCollection {
get { return this.myCollection_; }
}
This strategy is used for the Items
collections on the WindowsForms and WPF ItemsControl
controls, where users add and remove items they want the control to display. These controls publish the actual collection and use callbacks or event listeners to keep track of items.
此策略用于Items
WindowsForms 和 WPFItemsControl
控件上的集合,用户可以在其中添加和删除他们希望控件显示的项目。这些控件发布实际集合并使用回调或事件侦听器来跟踪项目。
WPF also exposes some settable collections to allow users to display a collection of items they control, such as the ItemsSource
property on ItemsControl
(option #3 from the original question). However, this is not a common use case.
WPF 还公开了一些可设置的集合,以允许用户显示他们控制的项目集合,例如ItemsSource
属性 on ItemsControl
(原始问题中的选项 #3)。然而,这不是一个常见的用例。
2)If users will only be reading data maintained by the object, then you can use a readonly collection, as Quibblesomesuggested:
2)如果用户只会读取对象维护的数据,那么您可以使用只读集合,如Quibblesome建议的那样:
private readonly List<T> myPrivateCollection_ = new ...;
private ReadOnlyCollection<T> myPrivateCollectionView_;
public ReadOnlyCollection<T> MyCollection {
get {
if( this.myPrivateCollectionView_ == null ) { /* lazily initialize view */ }
return this.myPrivateCollectionView_;
}
}
Note that ReadOnlyCollection<T>
provides a live view of the underlying collection, so you only need to create the view once.
请注意,它ReadOnlyCollection<T>
提供了底层集合的实时视图,因此您只需要创建一次视图。
If the internal collection does not implement IList<T>
, or if you want to restrict access to more advanced users, you can instead wrap access to the collection through an enumerator:
如果内部集合没有实现IList<T>
,或者如果您想限制对更高级用户的访问,您可以改为通过枚举器包装对集合的访问:
public IEnumerable<T> MyCollection {
get {
foreach( T item in this.myPrivateCollection_ )
yield return item;
}
}
This approach is simple to implement and also provides access to all the members without exposing the internal collection. However, it does require that the collection remain unmodfied, as the BCL collection classes will throw an exception if you try to enumerate a collection after it has been modified. If the underlying collection is likely to change, you can either create a light wrapper that will enumerate the collection safely, or return a copy of the collection.
这种方法实施起来很简单,而且还提供了对所有成员的访问,而无需公开内部集合。但是,它确实要求集合保持未修改状态,因为如果您在修改后尝试枚举集合,BCL 集合类将抛出异常。如果基础集合可能会更改,您可以创建一个轻量级包装器来安全地枚举该集合,或者返回该集合的副本。
3)Finally, if you need to expose arrays rather than higher-level collections, then you should return a copy of the array to prevent users from modifying it (option #2 from the orginal question):
3)最后,如果您需要公开数组而不是更高级别的集合,那么您应该返回数组的副本以防止用户修改它(原始问题中的选项#2):
private T[] myArray_;
public T[] GetMyArray( ) {
T[] copy = new T[this.myArray_.Length];
this.myArray_.CopyTo( copy, 0 );
return copy;
// Note: if you are using LINQ, calling the 'ToArray( )'
// extension method will create a copy for you.
}
You should not expose the underlying array through a property, as you will not be able to tell when users modify it. To allow modifying the array, you can either add a corresponding SetMyArray( T[] array )
method, or use a custom indexer:
您不应通过属性公开底层数组,因为您将无法判断用户何时修改它。要允许修改数组,您可以添加相应的SetMyArray( T[] array )
方法,或使用自定义索引器:
public T this[int index] {
get { return this.myArray_[index]; }
set {
// TODO: validate new value; raise change event; etc.
this.myArray_[index] = value;
}
}
(of course, by implementing a custom indexer, you will be duplicating the work of the BCL classes :)
(当然,通过实现自定义索引器,您将重复 BCL 类的工作:)
回答by Ryan Duffield
If you're simply looking to expose a collection on your instance, then using a getter/setter to a private member variable seems like the most sensible solution to me (your first proposed option).
如果您只是想在您的实例上公开一个集合,那么对私有成员变量使用 getter/setter 对我来说似乎是最明智的解决方案(您的第一个建议选项)。
回答by Quibblesome
I usually go for this, a public getter that returns System.Collections.ObjectModel.ReadOnlyCollection:
我通常会这样做,一个返回 System.Collections.ObjectModel.ReadOnlyCollection 的公共 getter:
public ReadOnlyCollection<SomeClass> Collection
{
get
{
return new ReadOnlyCollection<SomeClass>(myList);
}
}
And public methods on the object to modify the collection.
和对象上的公共方法来修改集合。
Clear();
Add(SomeClass class);
If the class is supposed to be a repository for other people to mess with then I just expose the private variable as per method #1 as it saves writing your own API, but I tend to shy away from that in production code.
如果该类应该是供其他人使用的存储库,那么我只是按照方法 #1 公开私有变量,因为它可以节省编写自己的 API,但我倾向于在生产代码中回避这一点。
回答by Telcontar
I'm a java developer but I think this is the same for c#.
我是一名 Java 开发人员,但我认为 c# 也是如此。
I never expose a private collection property because other parts of the program can change it without parent noticing, so that in the getter method I return an array with the objects of the collection and in the setter method I call a clearAll()
over the collection and then an addAll()
我从不公开私有集合属性,因为程序的其他部分可以在没有父通知的情况下更改它,因此在 getter 方法中我返回一个包含集合对象的数组,在 setter 方法中我调用clearAll()
了集合,然后调用了addAll()
回答by jpierson
Why do you suggest using ReadOnlyCollection(T) is a compromise? If you still need to get change notifications made on the original wrapped IList you could also use a ReadOnlyObservableCollection(T)to wrap your collection. Would this be less of a compromise in your scenario?
为什么你建议使用 ReadOnlyCollection(T) 是一种妥协?如果您仍然需要在原始包装的 IList 上获得更改通知,您还可以使用ReadOnlyObservableCollection(T)来包装您的集合。在您的情况下,这会不会是一种妥协?
回答by Johannes
ReadOnlyCollection still has the disadvantage that the consumer can't be sure that the original collection won't be changed at an inopportune time. Instead you can use Immutable Collections. If you need to do a change then instead changing the original you are being given a modified copy. The way it is implemented it is competitive with the performance of the mutable collections. Or even better if you don't have to copy the original several times to make a number of different (incompatible) changes afterwards to each copy.
ReadOnlyCollection 仍然有一个缺点,即使用者无法确定原始集合不会在不合时宜的时间更改。相反,您可以使用Immutable Collections。如果您需要进行更改,则改为更改原件,您将获得修改后的副本。它的实现方式与可变集合的性能具有竞争力。或者,如果您不必多次复制原件以在之后对每个副本进行许多不同的(不兼容的)更改,那就更好了。
回答by jbe
I recommend to use the new IReadOnlyList<T>
and IReadOnlyCollection<T>
Interfaces to expose a collection (requires .NET 4.5).
我建议使用 newIReadOnlyList<T>
和IReadOnlyCollection<T>
Interfaces 来公开集合(需要 .NET 4.5)。
Example:
例子:
public class AddressBook
{
private readonly List<Contact> contacts;
public AddressBook()
{
this.contacts = new List<Contact>();
}
public IReadOnlyList<Contact> Contacts { get { return contacts; } }
public void AddContact(Contact contact)
{
contacts.Add(contact);
}
public void RemoveContact(Contact contact)
{
contacts.Remove(contact);
}
}
If you need to guarantee that the collection can not be manipulated from outside then consider ReadOnlyCollection<T>
or the new Immutable collections.
如果您需要保证不能从外部操作该集合,则可以考虑ReadOnlyCollection<T>
使用新的 Immutable 集合。
Avoidusing the interface IEnumerable<T>
to expose a collection.
This interface does not define any guarantee that multiple enumerations perform well. If the IEnumerable represents a query then every enumeration execute the query again. Developers that get an instance of IEnumerable do not know if it represents a collection or a query.
避免使用接口IEnumerable<T>
公开集合。此接口不定义任何保证多个枚举执行良好。如果 IEnumerable 表示一个查询,那么每个枚举都会再次执行该查询。获得 IEnumerable 实例的开发人员不知道它是代表一个集合还是一个查询。
More about this topic can be read on this Wiki page.