前言

    软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类。不管是数组还是集合类,它们都有各自的优缺点。如何使用好集合是我们在开发过程中必须掌握的技巧。不要小看这些技巧,一旦在开发中使用了错误的集合或针对集合的方法,应用程序将会背离你的预想而运行。

  本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:

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

  建议21、选择正确的集合

  建议22、确保集合的线性安全

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

http://www.cnblogs.com/aehyok/p/3384637.html 这里有一篇文章,是我之前专门来介绍泛型的。我们应尽量的使用泛型集合。因为泛型的确有它的好处:

1、提供了类型安全,在编译期间就可以检查错误

2、更重要的是大部分情况下泛型集合的性能比非泛型集合的性能都高很多。

下面我们来看一段简单的测试性能的代码:

class Program{static int collectionCount = 0;static Stopwatch watch = null;static int testCount = 10000000;static void TestBegin(){GC.Collect(); ////强制对所有代码进行即时垃圾回收GC.WaitForPendingFinalizers();////挂起线程,执行终结器队列中的终结器(即析构方法)GC.Collect();///再次对所有代码进行垃圾回收,主要包括从终结器队列中出来的对象collectionCount = GC.CollectionCount(0);///返回在0代中执行的垃圾回收次数watch = new Stopwatch();watch.Start();}static void TestEnd(){watch.Stop();Console.WriteLine("耗时:{0}",watch.ElapsedMilliseconds.ToString());Console.WriteLine("垃圾回收次数:{0}", GC.CollectionCount(0) - collectionCount);}static void TestArrayList(){ArrayList arrayList = new ArrayList();int temp = 0;for (int i = 0; i < testCount; i++){arrayList.Add(i);temp = (int)arrayList[i];}arrayList = null;}static void TestGenericList(){List<int> list = new List<int>();int temp = 0;for (int i = 0; i < testCount; i++){list.Add(i);temp = list[i];}list = null;}static void Main(string[] args){Console.WriteLine("开始测试ArrayList");TestBegin();TestArrayList();TestEnd();Console.WriteLine("开始测试List<T>");TestBegin();TestGenericList();TestEnd();Console.ReadLine();}}

执行结果如下

  我上面测试的次数是10000000,可以发现,两者在垃圾回收次数和耗时都差距比较大,所以泛型集合有着非泛型集合无法超越的优势。所以还是尽量在我们的程序中使用泛型集合吧。

建议21、选择正确的集合

http://www.cnblogs.com/aehyok/p/3643928.html这里有一篇我刚写的关于集合的博文,主要是简单介绍了一下关于自己使用比较频繁的几个集合。

如果集合的数目固定并且不涉及转型,使用数组效率高,否则就是使用List<T>。

像使用数组、ArrayList、List<T>、Dictionary<key,value>这些集合的有点就是插入和删除数据效率比较高,缺点就是查找的效率相对来说低一些。

关于队列可以参考http://msdn.microsoft.com/zh-cn/library/System.Collections.Queue(v=vs.80).aspx

关于栈可以参考http://msdn.microsoft.com/zh-cn/library/System.Collections.Stack(v=vs.110).aspx

建议22、确保集合的线性安全

   建议18中提到,foreach循环不能代替for循环的一个原因是在迭代过程中对集合本身进行了增删操作。将此场景移植到多线程场景中,就是本建议要阐述的重点:确保集合的线程安全。集合线程安全是指在多个线程上添加活删除元素时,线程之间必须保持同步。

  下面我们来通过实例来更详细的查看一下,先简单定义一个实体类

    public class Person{public string Name { get; set; }public int Age { get; set; }}

       static List<Person> list = new List<Person>() { new Person(){ Name="aehyok",Age=25},new Person(){Name="Kris",Age=23},new Person(){Name="Leo",Age=26}};static AutoResetEvent autoSet = new AutoResetEvent(false);static void Main(string[] args){Thread t1 = new Thread(() => {///阻止当前线程
                autoSet.WaitOne();   foreach (var item in list){Console.WriteLine("t1:"+item.Name);Thread.Sleep(1000);}});t1.Start();Thread t2 = new Thread(() => { ///通知t1可以执行代码
                autoSet.Set();Thread.Sleep(1000);list.RemoveAt(2);});t2.Start();Console.ReadLine();}

再来简单分析一下这段代码,其实就是闲定义了一个List集合,然后又定义了一个 AutoRestEvent的实例,用于控制线程的。

接下来在Main函数中定义了两个线程,在线程一中将线程一暂停,然后当调用线程二的时候再来通知线程一继续运行。最终运行结果

主要是因为线程一在暂停之后,开始运行线程二随即线程一得到通知可以继续运行,通过代码可以发现都有Thread.Sleep(1000);也就是为了保证两个线程都还在运行期间,线程二移除了集合中的一个元素,那么当线程一再次循环的时候,导致了错误的发生。

早在泛型集合出现之前,非泛型集合一般会提供一个SyncRoot属性,要保证非泛型集合的线程安全,可以通过锁定该属性来实现。如果上面的集合用ArrayList代替,保证线程安全则应该在迭代和删除的时候都加上锁lock,代码如下所示:

        static ArrayList list = new ArrayList() { new Person(){ Name="aehyok",Age=25},new Person(){Name="Kris",Age=23},new Person(){Name="Leo",Age=26}};static AutoResetEvent autoSet = new AutoResetEvent(false);static void Main(string[] args){Thread t1 = new Thread(() => {///阻止当前线程
                autoSet.WaitOne();lock (list.SyncRoot){foreach (Person item in list){Console.WriteLine("t1:" + item.Name);Thread.Sleep(1000);}}});t1.Start();Thread t2 = new Thread(() => { ///通知t1可以执行代码
                autoSet.Set();Thread.Sleep(1000);lock (list.SyncRoot){list.RemoveAt(2);}});t2.Start();Console.ReadLine();}

运行结果就是线程一执行通过

如果你试过,那么会发现泛型集合没有这样的属性来进行加锁,必须要自己创建一个锁定对象来完成同步的任务。

所以第一个例子我们可以这样进行修改

 static List<Person> list = new List<Person>() { new Person(){ Name="aehyok",Age=25},new Person(){Name="Kris",Age=23},new Person(){Name="Leo",Age=26}};static object SyncObject = new object();static AutoResetEvent autoSet = new AutoResetEvent(false);static void Main(string[] args){Thread t1 = new Thread(() => {///阻止当前线程
                autoSet.WaitOne();lock (SyncObject){foreach (var item in list){Console.WriteLine("t1:" + item.Name);Thread.Sleep(1000);}}});t1.Start();Thread t2 = new Thread(() => { ///通知t1可以执行代码
                autoSet.Set();Thread.Sleep(1000);lock (SyncObject){list.RemoveAt(2);} });t2.Start();Console.ReadLine();}

英语小贴士

blind date——相亲

online session——在线会议

This depends on you ——这取决于你

I have a date with you——我和你有个约会

poor guy——可怜的家伙,也可以说成(a poor fish)

You look so tall——你看上去很高(形容人高不要用high)

Awesome——令人敬畏的; 使人畏惧的; 可怕的; 极好的

Awesome——听某某说也可以翻译成(So.Diao)

作者:aehyok

出处:http://www.cnblogs.com/aehyok/

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,那不妨点个推荐吧,谢谢支持:-O。

转载于:https://www.cnblogs.com/aehyok/p/3641896.html

编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]相关推荐

  1. 编写高质量代码改善C#程序的157个建议——建议148:不重复代码

    建议148:不重复代码 如果发现重复的代码,则意味着我们需要整顿一下,在继续前进. 重复的代码让我们的软件行为不一致.举例来说,如果存在两处相同的加密代码.结果在某一天,我们发现加密代码有个小Bug, ...

  2. 编写高质量代码改善C#程序的157个建议——建议86:Parallel中的异常处理

    建议86:Parallel中的异常处理 建议85阐述了如何处理Task中的异常.由于Task的Start方法是异步启动的,所以我们需要额外的技术来完成异常处理.Parallel相对来说就要简单很多,因 ...

  3. 编写高质量代码改善C#程序的157个建议——建议87:区分WPF和WinForm的线程模型...

    建议87:区分WPF和WinForm的线程模型 WPF和WinForm窗体应用程序都有一个要求,那就是UI元素(如Button.TextBox等)必须由创建它的那个线程进行更新.WinForm在这方面 ...

  4. 编写高质量代码改善C#程序的157个建议——建议127:用形容词组给接口命名

    建议127:用形容词组给接口命名 接口规范的是"Can do",也就是说,它规范的是类型可以具有哪些行为.所以,接口的命名应该是一个形容词,如: IDisposable表示可以被释 ...

  5. 编写高质量代码改善C#程序的157个建议——建议133:用camelCasing命名私有字段和局部变量...

    建议133:用camelCasing命名私有字段和局部变量 私有变量和局部变量只对本类型负责,它们在命名方式也采用和开放的属性及字段不同的方法.camelCasing很适合这类命名. camelCas ...

  6. 编写高质量代码改善C#程序的157个建议——建议104:用多态代替条件语句

    建议104:用多态代替条件语句 假设要开发一个自动驾驶系统.在设计之初,此自动驾驶系统拥有一个驾驶系统命令的枚举类型: enum DriveCommand{Start,Stop} 当前该枚举存在两个命 ...

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

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

  8. 编写高质量代码改善C#程序的157个建议——建议130:以复数命名枚举类型,以单数命名枚举元素...

    建议130:以复数命名枚举类型,以单数命名枚举元素 枚举类型应该具有负数形式,它表达的是将一组相关元素组合起来的语义.比如: enum Week{Monday,Tuesday,Wednesday,Th ...

  9. 编写高质量代码改善C#程序的157个建议——建议50:在Dispose模式中应区别对待托管资源和非托管资源...

    建议50:在Dispose模式中应区别对待托管资源和非托管资源 真正资源释放代码的那个虚方法是带一个bool参数的,带这个参数,是因为我们在资源释放时要区别对待托管资源和非托管资源. 提供给调用者调用 ...

  10. 编写高质量代码改善C#程序的157个建议——建议9: 习惯重载运算符

    建议9: 习惯重载运算符 在开发过程中,应该习惯于使用微软提供给我们的语法特性.我想每个人都喜欢看到这样的语法特性: int x = 1; int y = 2; int total = x + y; ...

最新文章

  1. 一堆乱七八糟绝不正经的排序算法
  2. 管理系统中的计算机应用信息可靠性,计算机信息处理系统的可靠性研究
  3. 恩布企业即时通讯软件,EntboostChat 1.4.2发布,iOS开源IM
  4. 关于网络编程中MTU、TCP、UDP、IP
  5. 如何维持手机电池寿命_充电小知识:你知道如何正确充电吗?这几种充电方式最损害电池...
  6. aix查看lv_Aix 添加VG,LV并挂载使用
  7. CMake常用命令整理
  8. 自学python需要什么_自学Python编程有什么要求
  9. sql语句用变量替换表名_使用内存优化表替换SQL临时表和表变量
  10. soap协议有get方式
  11. python excel操作xlwt_关于python操作excel,xlwt,xlwd,最简单的操作介绍
  12. 网摘Android调用WebService
  13. switchHost工具的使用
  14. 破解无线网络密码-BT3如何使用3
  15. 将内存FFFF:0 ~ FFFF:F 内存单元中的数据复制到 0:200 ~ 0:20F 中
  16. python 版权保护,python爬虫篇4——爬取专利著作权信息
  17. 最伟大的IT人物10强
  18. 树莓派wifi连接不上咋回事
  19. word转换成字符串
  20. Mdserver-web:一个开源、免费的 Linux 主机面板

热门文章

  1. 《C++(三)--多线程方法总结》
  2. 传奇进去选择服务器位置偏移,传奇聊天框偏移怎么解决,GOM GEE聊天框错位的解决方法...
  3. 数组的数据查找c语言,【查找数组面试题】面试问题:c语言实现数据… - 看准网...
  4. 自动驾驶 2-1 第 1 课补充阅读:传感器和计算硬件 -- 上
  5. 极客大学架构师训练营 JVM虚拟机原理 JVM垃圾回收原理 Java编程优化 秒杀 第九次作业
  6. 怎么解决IPA processing failed错误, 用xcode 11打包
  7. 易筋SpringBoot 2.1 | 第廿五篇:SpringBoot之Jedis访问Redis
  8. Command python setup.py egg_info failed with error code 1 in
  9. java多线程(2)----继承的方式创建多线程
  10. java传文件到kafka_Java将CSV的数据发送到kafka的示例