TL;DR,C# 编译器没有自动收集,因为有很多不同的公开收集的方式。公开集合时,您应该仔细考虑您希望如何封装集合并使用正确的方法。
C# 编译器提供自动属性的原因是因为它们很常见并且几乎总是以相同的方式工作,但是当您发现处理集合时情况很少那么简单 - 有 很多 暴露集合的不同方式,正确的方法总是取决于情况,仅举几例:
1) 可以更改的集合
通常没有真正需要对暴露的集合设置任何真正的限制:
public List<T> Collection
{
get
{
return this.collection;
}
set
{
if (value == null)
{
throw new ArgumentNullException();
}
this.collection = value;
}
}
private List<T> collection = new List<T>();
确保集合永远不会为空是个好主意,否则您可以只使用自动属性。除非我有充分的理由希望对我的集合进行更多封装,否则为了简单起见,我总是使用这种方法。
2) 可以修改但不能交换的集合
你可以用你喜欢的任何方式来编码,但想法是一样的——暴露的集合允许修改项目,但底层集合本身不能被另一个集合替换。例如:
public IList<T> Collection
{
get
{
return this.collection;
}
}
private ObservableCollection<T> collection = new ObservableCollection<T>();
在处理observable collections 之类的事情时,我倾向于使用这种简单的模式,当消费者应该能够修改集合但我已经订阅了更改通知 - 如果你让消费者交换整个集合,那么你只会导致头疼。
3) 公开一个集合的只读副本
您经常希望阻止消费者修改公开的集合 - 但通常您确实希望公开类能够修改该集合。一种简单的方法是公开收藏的只读副本:
public ReadOnlyCollection<T> Collection
{
get
{
return new ReadOnlyCollection<T>(this.collection);
}
}
private List<T> collection = new List<T>();
这带有返回的集合永远不会改变的属性,即使底层集合发生了变化。这通常是一件好事,因为它允许消费者遍历返回的集合而不用担心它可能会被更改:
foreach (var item in MyClass.Collection)
{
// This is safe - even if MyClass changes the underlying collection
// we won't be affected as we are working with a copy
}
但是,这并不总是预期的(或期望的)行为 - 例如 Controls property 不能以这种方式工作。您还应该考虑到以这种方式复制许多大型集合可能效率低下。
当公开只读集合时,请始终注意控件中的项目可以仍然可以修改。同样,这可能是一件好事,但如果您希望公开的集合“完全”不可修改,请确保集合中的项目也是只读/不可变的(例如System.String)。
4) 可以修改的集合,但只能以某种方式修改
假设您想公开一个可以添加但不能删除项目的集合?您可以在公开类本身上公开属性:
public ReadOnlyCollection<T> Collection
{
get
{
return new ReadOnlyCollection<T>(this.collection);
}
}
private List<T> collection = new List<T>();
public AddItem(T item);
但是,如果您的对象有很多这样的集合,那么您的界面很快就会变得混乱和混乱。我还发现这种模式有时可能违反直觉:
var collection = MyClass.Collection;
int count = collection.Count;
MyClass.AddItem(item);
Debug.Assert(collection.Count > count, "huh?");
这需要更多的努力,但 IMO 一种更简洁的方法是公开一个自定义集合,该集合封装了您的“真实”集合以及关于如何更改和不能更改该集合的规则,例如:
public sealed class CustomCollection<T> : IList<T>
{
private IList<T> wrappedCollection;
public CustomCollection(IList<T> wrappedCollection)
{
if (wrappedCollection == null)
{
throw new ArgumentNullException("wrappedCollection");
}
this.wrappedCollection = wrappedCollection;
}
// "hide" methods that don't make sense by explicitly implementing them and
// throwing a NotSupportedException
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException();
}
// Implement methods that do make sense by passing the call to the wrapped collection
public void Add(T item)
{
this.wrappedCollection.Add(item);
}
}
使用示例:
public MyClass()
{
this.wrappedCollection = new CustomCollection<T>(this.collection)
}
public CustomCollection<T> Collection
{
get
{
return this.wrappedCollection;
}
}
private CustomCollection<T> wrappedCollection;
private List<T> collection = new List<T>();
公开的集合现在封装了我们关于如何修改和不能修改集合的规则,并且还立即反映了对基础集合所做的更改(这可能是一件好事,也可能不是一件好事)。对于大型集合,它也可能更有效。