自3.0以来,C#具有很好的语法糖,如自动属性,这大大简化了封装原理的实现。如果将它与原子值一起使用,这是很好的,因此您可以替换如下封装模式:
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
只有一行:
public string FirstName { get; set; }
我非常喜欢这个伟大的特性,因为它节省了很多开发人员的时间。
但是,当您创建指向集合的属性时,情况就不太好了。通常,我看到集合属性是以两种方式之一实现的。
( 1)在没有自动属性的情况下,可以使用字段初始化器:
private List<string> _names = new List<string>();
public List<string> Names
{
get { return _names; }
}
( 2)使用汽车特性。如果类只有一个构造函数,则此方法是可以的:
public List<string> Names { get; private set; }
public .ctor()
{
Names = new List<string>();
}
但是,当您以这种方式处理诸如lists这样的可变集合时,就会破坏封装,因为该属性的用户可以修改集合而不让容器知道(如果忘记让setter私有,甚至可以替换集合)。
至于我,关于封装集合模式的正确实现,集合封装应该如下所示:
private readonly List<string> _names = new List<string>();
public ICollection<string> Names
{
get { return new ReadOnlyCollection<string>(_names); }
}
public void Add_Name(string name)
{
_names.Add(name);
}
public void Remove_Names(string name)
{
_names.Remove(name);
}
public void Clear_Names()
{
_names.Clear();
}
老实说,我不记得我是否在真正的代码中遇到过这种实现,甚至在框架源代码中也是如此。我认为这是因为人们懒惰,避免编写这么多的代码,只是为了使封装更强大一点。
我想知道为什么C#团队没有提供一些清晰而简单的方法来定义集合自动属性,这样开发人员就可以满足他们的懒惰,仍然创建健壮的代码?
发布于 2011-09-16 13:47:50
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>();
当使用者应该能够修改集合时,我倾向于在处理诸如可观察收藏这样的事情时使用这种简单的模式,但是我已经订阅了更改通知--如果允许消费者交换整个集合,那么只会引起头痛。
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
}
然而,这并不总是预期的(或期望的)行为--例如,属性不是这样工作的。您还应该考虑到,以这种方式复制许多大型集合可能效率低下。
公开仅读取的集合时,始终要注意控件中的项仍然可以修改。同样,这可能是一件好事,但是如果您希望公开的集合“完全”不可修改,那么确保集合中的项也是只读/不可变的(例如,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>();
公开的集合现在封装了我们关于集合如何能够和不能被修改的规则,并且立即反映了对底层集合所做的更改(这可能是一件好事,也可能不是好事)。对于大型藏品来说,它也可能更有效率。
发布于 2011-09-16 12:15:04
private IList<string> _list = new List<string>();
public IEnumerable<string> List
{
get
{
///return _list;
return _list.ToList();
}
}
发布于 2011-09-16 12:13:30
公开ICollection<string>
不是一个好主意,因为它允许添加和删除操作。
public sealed class CollectionHolderSample
{
private readonly List<string> names;
public CollectionHolderSample()
{
this.names = new List<string>();
}
public ReadOnlyCollection<string> Items
{
get
{
return this.names;
}
}
public void AddItem(string item)
{
this.names.Add(item);
}
}
编辑:
正如您所提到的,来自显式加()实现的移除()和ReadOnlyCollection<T>.ICollection<T>
方法将抛出NotSupportedException
异常,因此集合是只读的。
此外,为了确保隐藏下的集合是只读的,您可以检查IsReadOnly性质。
MSDN说:
如果ReadOnlyCollection.ICollection.IsReadOnly属性为 true,则为只读;否则为false。在ReadOnlyCollection的默认实现中,此属性始终返回true。
https://stackoverflow.com/questions/7444395
复制相似问题