你如何公开一个集合完全取决于用户如何打算与它进行交互。
1)如果用户将被添加和从对象的集合中删除项目,那么一个简单的只得到收集属性是最好的(选择#1从原来的问题):
private readonly Collection<T> myCollection_ = new ...;
public Collection<T> MyCollection {
get { return this.myCollection_; }
}
这一战略用于WindowsForms和WPF ItemsControl
控件上的Items
集合,其中用户添加和删除他们希望控件显示的项目。这些控件发布实际的集合并使用回调或事件侦听器来跟踪项目。
WPF还公开了一些可设置的集合,以允许用户显示它们控制的项目的集合,例如ItemsControl
(原始问题中的选项#3)上的ItemsSource
属性。但是,这不是一个常见的用例。
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_;
}
}
注意ReadOnlyCollection<T>
提供了一个实时取景基础集合,所以你只需要创建一次视图。
如果内部收集未实现IList<T>
,或者如果你想限制访问更高级的用户,可以改为通过枚举包装集合访问:
public IEnumerable<T> MyCollection {
get {
foreach(T item in this.myPrivateCollection_)
yield return item;
}
}
这种方法实现起来很简单并且还提供了访问所有成员而不暴露内部收藏。但是,它确实要求集合保持未修改状态,因为如果尝试在修改集合后枚举集合,则BCL集合类将引发异常。如果底层集合可能发生变化,您可以创建一个轻型包装器来安全地枚举集合,或返回集合的副本。
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.
}
你不应该通过属性暴露底下阵列,因为你将不能够告诉当用户修改它。为了让修改数组,你可以添加相应的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;
}
}
(当然,通过实现自定义索引,你会复制BCL类的工作:)
这会在每次读取Collection属性时创建新的ReadOnlycollection,这可能非常耗费资源 – Ivan 2009-09-04 16:00:54
Aye,Emperor XLII改进了他发布的上述示例中的前提。 – Quibblesome 2009-09-11 14:25:49