继承 List<T> 实现集合是一个糟糕的主意吗?

14

我曾经阅读过Imaar Spaanjars的一篇文章,介绍如何构建三层应用程序。(http://imar.spaanjaars.com/416/building-layered-web-applications-with-microsoft-aspnet-20-part-1)这篇文章成为我的编码基础已经有一段时间了。

因此,我像他一样通过继承List<T>来实现集合。所以如果我有一个名为Employee的类,为了实现一个集合,我也会有一个名为Employees的类,如下所示。

class Employee
{
   int EmpID {get;set;}
   string EmpName {get;set;}  

}

class Employees : List<Employee>
{
   public Employees(){}
}

我从来没有真正质疑过这一点,因为它可以为我完成工作。但是现在我开始尝试一些东西,我不确定这是否是正确的方法。

例如,如果我想从员工中获取一个子集,比如

 Employees newEmployees = (Employees) AllEmployees.FindAll(emp => emp.JoiningDate > DateTime.Now);

这会抛出一个 System.InvalidCastException 异常。但是,如果我使用以下代码,则不会出现问题。

List<Employee> newEmployees = AllEmployees.FindAll(emp => emp.JoiningDate > DateTime.Now);

那么我如何实现员工(Employees),以便在我的 DAL 或 BLL 中不必显式使用 List<Employee>?或者,我该如何摆脱 InvalidCastexception 异常?

4个回答

19

我不建议继承 List<T>,因为它会引入这样的问题,并且并没有真正帮助(因为没有virtual方法可以覆盖)。我建议要么使用 List<T> (或更抽象的 IList<T>),要么引入多态性可以使用 Collection<T>,因为它虚拟方法。

另外需要注意的是,在处理像FindAll这样的事情时,你可能会发现 LINQ 选项(如 .Where())也是很有用的替代品;尤其是,它们适用于任何 IList<T> (或 IEnumerable<T>),而不仅仅是List<T>和其子类。


没错,但我有一个小疑问。我有多个项目,如DAL和BLL,实体和PL。实体是我定义类(如员工)的地方,然后在其他项目中使用它们。这样做的想法是每个项目都可以独立于其他项目进行开发。是否有可能开发人员在BLL中使用IList<Employee>,而另一个开发人员在DAL中使用Collection<Employee>,从而导致问题。关键是在实体中定义实现,并让其他人在需要时使用它。 - shashi
@sassyboy - 由于 Collection<T> 实现了 IList<T>,所以这通常不是问题。最终,通过子类化 List<T> 并没有真正定义一个适当的实现,因为这对于 T 特定的逻辑没有任何帮助。 - Marc Gravell

11
你通过子类化 List<Employee> 获得了什么好处?如果没有任何增益,那么这就是不必要的代码膨胀。如果你没有显示方法或通过构造函数强制合同(在此处未显示),那么子类化可能有一个有效的理由。
就解决转换问题而言,你无法从 List<Employee> 向下转换为 Employees,因为 List<Employee> 不继承自 Employees。如果你需要返回符合你条件的 Employees,则最好将调用封装起来,并将返回的列表项插入到你自己的 Employees 对象中。除非像我上面所说的那样,你有一个充分的理由去子类化List<Employee>,否则这似乎是浪费时间。
个人认为,在可能的情况下,应该尽量使用 IList<T>,不要创建子类,除非它们有存在的理由。

4
我个人会将列表存储为容器对象的成员,如有必要,会提供获取列表本身的访问器。
class MyCollection<T> 
{
    List<T> _table;
    public List<T> Table
    {
        get
        {
            return _table;
        }
    }
    // ... other access/utility functions common for all tables 
    //     (CRUD for example)
}

class Employees : MyCollection<Employee>
{
    // ... yet more methods 
}

我必须承认,我这样做是为了提供自己的方法,例如:

T FindById(int id);
void Add(T entity);
void Remove(T entity);

关于基类和:

T[] GetEmployeesBy(your_filter_here);

我仍在使用 .net 2.0,所以我没有 Linq - 或许这就是它出现问题的原因。


4
一个简单的经验法则是从组合开始(例如将Employees包装在一个通用集合中),而不是从继承开始。从继承开始的设计将把自己限制在死角里。组合更加灵活和可修改。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接