我想知道哪一个被认为是最干净或最好用的,为什么。
其中一个公开了一个乘客列表,让用户添加和删除等。另一个隐藏列表,只让用户枚举它们并使用特殊方法添加。
示例1
class Bus
{
public IEnumerable<Person> Passengers { get { return passengers; } }
private List<Passengers> passengers;
public Bus()
{
passengers = new List<Passenger>();
}
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<Person> Passengers { get; private set; }
public Bus()
{
Passengers = new List<Passenger>();
}
}
var bus = new Bus();
bus.Passengers.Add(new Passenger());
foreach(var passenger in bus.Passengers)
Console.WriteLine(passenger);
我要说的第一堂课是更好的封装。在这个确切的情况下,这可能是更好的方法(因为你应该确保它的空间留在公交车上,等等)。但我想可能会出现第二类也有用的情况?就好像这个类并不真正关心那个列表会发生什么,只要它有一个。你觉得怎么样?
答案 0 :(得分:6)
在示例一中,可以改变您的收藏。
请考虑以下事项:
var passengers = (List<Passenger>)bus.Passengers;
// Now I have control of the list!
passengers.Add(...);
passengers.Remove(...);
要解决此问题,您可能会考虑这样的事情:
class Bus
{
private List<Passenger> passengers;
// Never expose the original collection
public IEnumerable<Passenger> Passengers
{
get { return passengers.Select(p => p); }
}
// Or expose the original collection as read only
public ReadOnlyCollection<Passenger> ReadOnlyPassengers
{
get { return passengers.AsReadOnly(); }
}
public void AddPassenger(Passenger passenger)
{
passengers.Add(passenger);
}
}
答案 1 :(得分:0)
在大多数情况下,我会认为示例2是可接受的,前提是底层类型是可扩展的和/或暴露某种形式的onAdded / onRemoved事件,以便您的内部类可以响应对集合的任何更改。
在这种情况下,List&lt; T&gt;不合适,因为班级无法知道是否添加了某些内容。相反,你应该使用Collection,因为Collection&lt; T&gt; class有几个虚拟成员(Insert,Remove,Set,Clear),可以覆盖这些成员并添加事件触发器以通知包装类。
(您还必须注意,该类的用户可以修改列表/集合中的项目而父类不知道它,因此请确保您不依赖于未更改的项目 - 除非他们显然是不可变的 - 或者如果需要,你可以提供onChanged风格的事件。)
答案 2 :(得分:0)
通过FxCop运行您的相应示例,这应该为您提供有关暴露List<T>
的风险的提示
答案 3 :(得分:0)
我会说这一切都取决于你的情况。我通常会选择2,因为它是最简单的,除非你有商业理由为它添加更严格的控制。
答案 4 :(得分:0)
选项2是最简单的,但是允许其他类向集合添加/删除元素,这可能很危险。
我认为一个好的启发式方法是考虑包装器方法的作用。如果您的AddPassenger(或Remove或其他)方法只是将调用中继到集合,那么我会选择更简单的版本。如果你必须在插入之前检查元素,那么选项1基本上是不可避免的。如果您必须跟踪插入/删除的元素,您可以采用任何一种方式。使用选项2,您必须在集合上注册事件以获取通知,并且使用选项1,您必须为列表中要使用的每个操作创建包装器(例如,如果您想要插入以及添加),所以我猜这取决于。