建议20:使用泛型集合代替非泛型集合

在建议1中我们知道,如果要让代码高效运行,应该尽量避免装箱和拆箱,以及尽量减少转型。很遗憾,在微软提供给我们的第一代集合类型中没有做到这一点,下面我们看ArrayList这个类的使用情况:

  1. ArrayList al=new ArrayList();
  2. al.Add();
  3. al.Add();
  4. al.Add("mike");
  5. foreach (var item in al)
  6. {
  7. Console.WriteLine(item);
  8. }

上面这段代码充分演示了我们可以将程序写得多么糟糕。首先,ArrayList的Add方法接受一个object参数,所以al.Add(1)首先会完成一次装箱;其次,在foreach循环中,待遍历到它时,又将完成一次拆箱。在这段代码中,整形和字符串作为值类型和引用类型,都会先被隐式地强制转型为object,然后在foreach循环中又被转型回来。同时,这段代码也是非类型安全的:我们然ArrayList同时存储了整型和字符串,但是缺少编译时的类型检查。虽然有时候需要有意这样去实现,但是更多的时候,应该尽量避免。缺少类型检查,在运行时会带来隐含的Bug。集合类ArrayList如果进行如下所示的运算,就会抛出一个IvalidCastException:

  1. ArrayList al=new ArrayList();
  2. al.Add();
  3. al.Add();
  4. al.Add("mike");
  5. int t = ;
  6. foreach (int item in al)
  7. {
  8. t += item;
  9. }

ArrayList同时还提供了一个带ICollection参数的构造方法,可以直接接收数组,如下所示:

  1. var intArr = new int[] {, , , };
  2. ArrayList al=new ArrayList(intArr);

该方法内部实现一样糟糕,如下所示(构造方法内部最终调用了下面的InsertRange方法):

  1. public virtual void InsertRange(int index, ICollection c)
  2. {
  3. if (c == null)
  4. {
  5. throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
  6. }
  7. if ((index < ) || (index > this._size))
  8. {
  9. throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  10. }
  11. int count = c.Count;
  12. if (count > )
  13. {
  14. this.EnsureCapacity(this._size + count);
  15. if (index < this._size)
  16. {
  17. Array.Copy(this._items, index, this._items, index + count, this._size - index);
  18. }
  19. object[] array = new object[count];
  20. c.CopyTo(array, );
  21. array.CopyTo(this._items, index);
  22. this._size += count;
  23. this._version++;
  24. }
  25. }

概括来讲,如果对大型集合进行循环访问、转型或装箱和拆箱操作,使用ArrayList这样的传统集合对效率影响会非常大。鉴于此,微软提供了对泛型的支持。泛型使用一对<>括号将实际类型括起来,然后编译器和运行时会完成剩余的工作。微软也不建议大家使用ArrayList这样的类型了,转而建议使用它们的泛型实现,如List<T>。

注意,非泛型集合在System.Collections命名空间下,对应的泛型集合则在System.Collections.Generic命名空间下。

建议一开始的那段代码的泛型实现为:

  1. List<int> intList = new List<int>();
  2. intList.Add();
  3. intList.Add();
  4. //intList.Add("mike");
  5. foreach (var item in intList)
  6. {
  7. Console.WriteLine(item);
  8. }

代码中被注释的那一行不会被编译通过,因为“mike"不是整型,这里就体现了类型安全的特点。

下面比较了非泛型集合和泛型集合在运行中的效率:

  1. static void Main(string[] args)
  2. {
  3. Console.WriteLine("开始测试ArrayList:");
  4. TestBegin();
  5. TestArrayList();
  6. TestEnd();
  7. Console.WriteLine("开始测试List<T>:");
  8. TestBegin();
  9. TestGenericList();
  10. TestEnd();
  11. }
  12. static int collectionCount = ;
  13. static Stopwatch watch = null;
  14. static int testCount = ;
  15. static void TestBegin()
  16. {
  17. GC.Collect(); //强制对所有代码进行即时垃圾回收
  18. GC.WaitForPendingFinalizers(); //挂起线程,执行终结器队列中的终结器(即析构方法)
  19. GC.Collect(); //再次对所有代码进行垃圾回收,主要包括从终结器队列中出来的对象
  20. collectionCount = GC.CollectionCount(); //返回在0代码中执行的垃圾回收次数
  21. watch = new Stopwatch();
  22. watch.Start();
  23. }
  24.  
  25. static void TestEnd()
  26. {
  27. watch.Stop();
  28. Console.WriteLine("耗时:" + watch.ElapsedMilliseconds.ToString());
  29. Console.WriteLine("垃圾回收次数:" + (GC.CollectionCount() - collectionCount));
  30. }
  31.  
  32. static void TestArrayList()
  33. {
  34. ArrayList al = new ArrayList();
  35. int temp = ;
  36. for (int i = ; i < testCount; i++)
  37. {
  38. al.Add(i);
  39. temp = (int)al[i];
  40. }
  41. al = null;
  42. }
  43.  
  44. static void TestGenericList()
  45. {
  46. List<int> listT = new List<int>();
  47. int temp = ;
  48. for (int i = ; i < testCount; i++)
  49. {
  50. listT.Add(i);
  51. temp = listT[i];
  52. }
  53. listT = null;
  54. }

输出为:

开始测试ArrayList:

耗时:2375

垃圾回收次数:26

开始测试List<T>:

耗时:220

垃圾回收次数:5

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

编写高质量代码改善C#程序的157个建议——建议20:使用泛型集合代替非泛型集合的更多相关文章

  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. 关于android方向传感器的使用

    Android2.2以后 orientation sensors 就被deprecated了 官方建议用acceleration and magnetic sensor 来算 关于这个问题,CSDN上 ...

  2. Zombie进程

    fork()的作用就是创建一个该进程下的子进程,在其exit 或 exec之前,和他共享代码,以一个父进程的身份,完成以下工作: 1.分配标识符pid和PCB. 2.让子进程复制父进程的环境. 3.给 ...

  3. Ubuntu 14.04报“leaking memory”错误

    在做一些实验的时候,临时配置了笔记本网卡eth0的IP地址,结果出现了以下错误, $ sudo ifconfig eth0 192.168.2.100/24 no talloc stackframe  ...

  4. AngularJS:教程

    ylbtech-AngularJS:教程 1.返回顶部 1. AngularJS 教程 AngularJS 通过新的属性和表达式扩展了 HTML. AngularJS 可以构建一个单一页面应用程序(S ...

  5. windows下配置protobuf2.6.1

    步骤: 下载protobuf-2.6.1.zip和protoc-2.6.1-win32.zip,地址:https://github.com/google/protobuf/tags 到目录protob ...

  6. 实战MvcPager(PagerOptions自定义样式&同、异步)

    ASP.NET MVC下的分页控件MvcPager用起来简直太嗨呸了,两句代码实现一个分页,而且需要改变样式的时候直接构造PagerOptions类 实战无需多说,拿来用之即可.个人觉得对性能影响不大 ...

  7. python's twenty-seventh day for me 模

    time模块: 1,计算执行代码的时间 time.time() 2,让程序停这里一段时间 time.sleep(时间(s)) 时间戳时间: import time print(time.time()) ...

  8. python's twenty-sixth day for me 模块

    configparser 模块: 该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键 = 值). 创建文件: # 创建文件 i ...

  9. java成神之——date和calendar日期的用法

    date和calendar日期的用法 util的data转换成sql的data 创建Date对象 格式化 Instant ChronoUnit LocalTime LocalDate LocalDat ...

  10. java-虚拟机-索引

    底层 JVM之堆内存(年经代,老年代) Java内存泄露的理解与解决 内存溢出和内存泄漏的区别.产生原因以及解决方案 JVM内容梳理 Jvm的体系结构