【问题标题】:C#: Encapsulation of for example collectionsC#:例如集合的封装
【发布时间】:2009-08-05 08:21:24
【问题描述】:

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

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

示例 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);

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

【问题讨论】:

标签: c# class encapsulation


【解决方案1】:

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

考虑以下几点:

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);
  }
 }

【讨论】:

    【解决方案2】:

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

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

    (您还必须注意,该类的用户可以在父类不知道的情况下修改列表/集合中的项目,因此请确保您不要依赖未更改的项目 - 除非他们显然是不可变的 - 或者如果需要,您可以提供 onChanged 样式事件。)

    【讨论】:

      【解决方案3】:

      通过 FxCop 运行您各自的示例,这应该会提示您暴露 List&lt;T&gt; 的风险

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 最好预先正确封装它,除非您有商业理由不这样做。考虑一下会发生什么,当 6 个月后,突然引入了关于可以将项目添加到集合中的条件的新业务规则。现在您必须重构所有客户端代码,而不仅仅是一个类。
        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-01-06
          • 1970-01-01
          • 1970-01-01
          • 2011-05-06
          • 2011-12-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多