建议23:避免将List<T>作为自定义集合类的基类
2016-09-01 13:27
232 查看
建议23:避免将List<T>作为自定义集合类的基类
如果要实现一个自定义的集合类,不应该以一个FCL集合类为基类,反而应扩展相应的泛型接口。FCL结合类应该以组合的形式包含至自定义的集合类,需要扩展的泛型接口通常是IEnumerable<T>和ICollection<T>(或ICollection<T>的子接口,如IList<T>),前者规范了集合类的迭代功能,后者规范了一个集合通常会有的操作。
一般的情况下,下面两个实现的集合类都能完成默认的需求:
不过,List<T>基本上没有提供可供子类使用的protected成员(从object中继承的Finalize和MemberwiseClone方法除外),所以继承List<T>并没有带来任何继承上的优势,反而丧失了面向接口编程的灵活性。稍加不注意,隐含的Bug就会接踵而至。
以Employees1为例,如果要在Add方法中加入某些需求方面的变化,比如,为名字添加一个后缀“Changed!",但是客户端的开发人员也许已经习惯了面向接口编程的方式,他在为集合添加一个元素是使用了如下的语法:
于是,代码的实际输出会偏离集合类设计者的设想。代码输出为:
Mike Changed!
Rose Changed!
Steve
要纠正这类行为,应该采用Employees2的方式:
输出结果为:
Mike Changed!
Rose Changed!
Steve Changed!
转自:《编写高质量代码改善C#程序的157个建议》陆敏技
如果要实现一个自定义的集合类,不应该以一个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>作为自定义集合类的基类
- 编写高质量代码改善C#程序的157个建议[勿选List<T>做基类、迭代器是只读的、慎用集合可写属性]
- 编写高质量代码改善C#程序的157个建议[勿选List<T>做基类、迭代器是只读的、慎用集合可写属性]
- AutoCompleteTextView 自定义adapter。 可以添加List<Map<String,Object>>
- List<T>的ForEach方法(可以使用“语句lambda”作为其参数),如下例子:
- 自定义ORM系列(一)利用attribute实现简单的reader=>entity和reader=>List<entity>映射
- 关于Ibatis.net ,将List<T> 作为参数的学习笔记
- List<T>集合类
- C#泛型集合类介绍之List<T>(转载+编辑)
- 泛型List<T>作为DataGriView数据源
- List<Map>自定义Comparator在jdk1.7中出现问题
- .net集合类的研究--Array,ArrayList,List<T>
- <STL初探>list节点为自定义类型,如何访问节点中变量和函数,如何使用list自带的排序
- 建议采用的顺序是 List<T>、List<?>、List<Object>
- 使用BindingList<>作为DataGridView控件的数据源
- C#中如何将List<自定义>转为Json格式 及相关函数-DataContractJsonSerializer
- hander message 传自定义List<Object>
- List<T>泛型数组API自带的Sort() 排序方法根据自定义排序实现
- IEnumerable<T>作为方法返回值类型——建议通过yield return返回
- 编写高质量代码改善C#程序的157个建议[IEnumerable<T>和IQueryable<T>、LINQ避免迭代、LINQ替代迭代]