建议23:避免将List<T>作为自定义集合类的基类

如果要实现一个自定义的集合类,不应该以一个FCL集合类为基类,反而应扩展相应的泛型接口。FCL结合类应该以组合的形式包含至自定义的集合类,需要扩展的泛型接口通常是IEnumerable<T>和ICollection<T>(或ICollection<T>的子接口,如IList<T>),前者规范了集合类的迭代功能,后者规范了一个集合通常会有的操作。

一般的情况下,下面两个实现的集合类都能完成默认的需求:

class Employees1 : List<Employee>
class Employees2 : IEnumerable<Employee>, ICollection<Employee>

不过,List<T>基本上没有提供可供子类使用的protected成员(从object中继承的Finalize和MemberwiseClone方法除外),所以继承List<T>并没有带来任何继承上的优势,反而丧失了面向接口编程的灵活性。稍加不注意,隐含的Bug就会接踵而至。

以Employees1为例,如果要在Add方法中加入某些需求方面的变化,比如,为名字添加一个后缀“Changed!",但是客户端的开发人员也许已经习惯了面向接口编程的方式,他在为集合添加一个元素是使用了如下的语法:

        static void Main(string[] args)
{
Employees1 employees1 = new Employees1()
{
new Employee(){ Name = "Mike" },
new Employee(){ Name = "Rose" }
};
IList<Employee> employees = employees1;
employees.Add(new Employee() { Name = "Steve" });
foreach (var item in employees1)
{
Console.WriteLine(item.Name);
}
} class Employee
{
public string Name { get; set; }
} class Employees1 : List<Employee>
{
public new void Add(Employee item)
{
item.Name += " Changed!";
base.Add(item);
}
}

于是,代码的实际输出会偏离集合类设计者的设想。代码输出为:

Mike Changed!
Rose Changed!
Steve

要纠正这类行为,应该采用Employees2的方式:

        static void Main(string[] args)
{
Employees2 employees2 = new Employees2()
{
new Employee(){ Name = "Mike" },
new Employee(){ Name = "Rose" }
};
ICollection<Employee> employees = employees2;
employees.Add(new Employee() { Name = "Steve" });
foreach (var item in employees2)
{
Console.WriteLine(item.Name);
}
} class Employees2 : IEnumerable<Employee>, ICollection<Employee>
{
List<Employee> items = new List<Employee>(); #region IEnumerable<Employee> 成员 public IEnumerator<Employee> GetEnumerator()
{
return items.GetEnumerator();
} #endregion #region ICollection<Employee> 成员 public void Add(Employee item)
{
item.Name += " Changed!";
items.Add(item);
} //省略 #endregion
}

输出结果为:

Mike Changed!
Rose Changed!
Steve Changed!

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

编写高质量代码改善C#程序的157个建议——建议23:避免将List<T>作为自定义集合类的基类的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  2. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  3. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  4. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  5. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  6. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  7. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

  8. 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释

    建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...

  9. 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量

    建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...

  10. 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法

    建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...

随机推荐

  1. idea的有些路径问题

    resource文件在xml的路径 http://www.cnblogs.com/zqr99/p/7642712.html resource文件在io流的读取 this.getClass().getR ...

  2. bzoj 4032 [HEOI2015]最短不公共子串——后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4032 不是 b 的子串的话就对 b 建后缀自动机,在 a 上枚举从每个位置开始的子串或者找子 ...

  3. Linux:远程连接 SSH

    一.认识 SSH 定义 SSH(Secure shell):安全外壳协议:是建立在应用层基础上的安全协议: 通过 SSH 进行服务端连接,不容易被窃取信息: 连接服务器 ssh 服务器名 + @ + ...

  4. 【转】Jenkins+Ant+Jmeter搭建持续集成的接口测试平台

    一.什么是接口测试? 接口测试是测试系统组件间接口的一种测试.接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点.测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻 ...

  5. Java-Runoob:Java 条件语句

    ylbtech-Java-Runoob:Java 条件语句 1.返回顶部 1. Java 条件语句 - if...else 一个 if 语句包含一个布尔表达式和一条或多条语句. 语法 if 语句的语法 ...

  6. xml处理模块

    xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的 ...

  7. thinkphp使用自定义类方法

    1.通过Model调用 <?php /** * 积分模型 api接口 */ class ApiModel{ private $url = 'http://js.yunlutong.com/Cus ...

  8. PCB上 如何显示 汉字

    原理图上有汉字,那如何在PCB上显示汉子呢  ?  而不至于显示乱码 按如下操作  ,双击乱码  ,进入设置模式 设置好后,显示的字体样式.

  9. Windows可信任路径代码执行漏洞

    乌云里有很多这样的案例,当然在开发过程中也会存在这样的问题 搜索:可信任路径漏洞 调用案例中的描述: Microsoft Windows API使用CreateProcess()函数创建新的进程及其主 ...

  10. jQ版大图滚动

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...