C#:封装例如集合

我想知道哪一个被认为是最干净或最好用的,为什么。

其中一个公开了一个乘客列表,让用户添加和删除等。另一个隐藏列表,只让用户枚举它们并使用特殊方法添加。

例1

class Bus { public IEnumerable Passengers { get { return passengers; } } private List passengers; public Bus() { passengers = new List(); } public void AddPassenger(Passenger passenger) { passengers.Add(passenger); } } var bus = new Bus1(); bus.AddPassenger(new Passenger()); foreach(var passenger in bus.Passengers) Console.WriteLine(passenger); 

例2

 class Bus { public List Passengers { get; private set; } public Bus() { Passengers = new List(); } } var bus = new Bus(); bus.Passengers.Add(new Passenger()); foreach(var passenger in bus.Passengers) Console.WriteLine(passenger); 

我要说的第一堂课是更好的封装。 在这个确切的情况下,这可能是更好的方法(因为你应该确保它的空间留在公交车上,等等)。 但我想可能会出现第二类也有用的情况? 就好像这个class级并不真正关心那个列表会发生什么,只要它有一个。 你怎么看?

在示例一中,可以改变您的集合。

考虑以下:

 var passengers = (List)bus.Passengers; // Now I have control of the list! passengers.Add(...); passengers.Remove(...); 

要解决这个问题,你可能会考虑这样的事情:

 class Bus { private List passengers; // Never expose the original collection public IEnumerable Passengers { get { return passengers.Select(p => p); } } // Or expose the original collection as read only public ReadOnlyCollection ReadOnlyPassengers { get { return passengers.AsReadOnly(); } } public void AddPassenger(Passenger passenger) { passengers.Add(passenger); } } 

在大多数情况下,我会认为示例2是可接受的,前提是底层类型是可扩展的和/或暴露某种forms的onAdded / onRemoved事件,以便您的内部类可以响应对集合的任何更改。

在这种情况下,List 不适合,因为课程无法知道是否添加了某些内容。 相反,您应该使用Collection,因为Collection 类具有多个可以被覆盖的虚拟成员(Insert,Remove,Set,Clear),并且添加了事件触发器以通知包装类。

(您还必须要注意,类的用户可以修改列表/集合中的项目而父类不知道它,因此请确保您不依赖于未更改的项目 – 除非它们显然是不可变的 – 或者如果需要,您可以提供onChanged风格的活动。)

通过FxCop运行您的相应示例,这应该给您一个关于暴露List的风险的提示

我会说这一切都取决于你的情况。 我通常会选择2,因为它是最简单的, 除非你有商业理由为它添加更严格的控制。

选项2是最简单的,但允许其他类向集合添加/删除元素,这可能很危险。

我认为一个好的启发式方法是考虑包装器方法的作用。 如果您的AddPassenger(或Remove或其他)方法只是将调用中继到集合,那么我会选择更简单的版本。 如果你必须插入之前检查元素,那么选项1基本上是不可避免的。 如果您必须跟踪插入/删除的元素,您可以采用任何一种方式。 使用选项2,您必须在集合上注册事件以获取通知,并且使用选项1,您必须为列表中要使用的每个操作创建包装器(例如,如果您想要插入以及添加),所以我猜这取决于。