编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]
前言
软件开发过程中,不可避免会用到集合,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 = ;
- static Stopwatch watch = null;
- static int testCount = ;
- static void TestBegin()
- {
- GC.Collect(); ////强制对所有代码进行即时垃圾回收
- GC.WaitForPendingFinalizers();////挂起线程,执行终结器队列中的终结器(即析构方法)
- GC.Collect();///再次对所有代码进行垃圾回收,主要包括从终结器队列中出来的对象
- collectionCount = GC.CollectionCount();///返回在0代中执行的垃圾回收次数
- watch = new Stopwatch();
- watch.Start();
- }
- static void TestEnd()
- {
- watch.Stop();
- Console.WriteLine("耗时:{0}",watch.ElapsedMilliseconds.ToString());
- Console.WriteLine("垃圾回收次数:{0}", GC.CollectionCount() - collectionCount);
- }
- static void TestArrayList()
- {
- ArrayList arrayList = new ArrayList();
- int temp = ;
- for (int i = ; i < testCount; i++)
- {
- arrayList.Add(i);
- temp = (int)arrayList[i];
- }
- arrayList = null;
- }
- static void TestGenericList()
- {
- List<int> list = new List<int>();
- int temp = ;
- for (int i = ; 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=},
- new Person(){Name="Kris",Age=},
- new Person(){Name="Leo",Age=}
- };
- 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();
- }
- });
- t1.Start();
- Thread t2 = new Thread(() =>
- {
- ///通知t1可以执行代码
- autoSet.Set();
- Thread.Sleep();
- list.RemoveAt();
- });
- t2.Start();
- Console.ReadLine();
- }
再来简单分析一下这段代码,其实就是闲定义了一个List集合,然后又定义了一个 AutoRestEvent的实例,用于控制线程的。
接下来在Main函数中定义了两个线程,在线程一中将线程一暂停,然后当调用线程二的时候再来通知线程一继续运行。最终运行结果
主要是因为线程一在暂停之后,开始运行线程二随即线程一得到通知可以继续运行,通过代码可以发现都有Thread.Sleep(1000);也就是为了保证两个线程都还在运行期间,线程二移除了集合中的一个元素,那么当线程一再次循环的时候,导致了错误的发生。
早在泛型集合出现之前,非泛型集合一般会提供一个SyncRoot属性,要保证非泛型集合的线程安全,可以通过锁定该属性来实现。如果上面的集合用ArrayList代替,保证线程安全则应该在迭代和删除的时候都加上锁lock,代码如下所示:
- static ArrayList list = new ArrayList()
- {
- new Person(){ Name="aehyok",Age=},
- new Person(){Name="Kris",Age=},
- new Person(){Name="Leo",Age=}
- };
- 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();
- }
- }
- });
- t1.Start();
- Thread t2 = new Thread(() =>
- {
- ///通知t1可以执行代码
- autoSet.Set();
- Thread.Sleep();
- lock (list.SyncRoot)
- {
- list.RemoveAt();
- }
- });
- t2.Start();
- Console.ReadLine();
- }
运行结果就是线程一执行通过
如果你试过,那么会发现泛型集合没有这样的属性来进行加锁,必须要自己创建一个锁定对象来完成同步的任务。
所以第一个例子我们可以这样进行修改
- static List<Person> list = new List<Person>()
- {
- new Person(){ Name="aehyok",Age=},
- new Person(){Name="Kris",Age=},
- new Person(){Name="Leo",Age=}
- };
- 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();
- }
- }
- });
- t1.Start();
- Thread t2 = new Thread(() =>
- {
- ///通知t1可以执行代码
- autoSet.Set();
- Thread.Sleep();
- lock (SyncObject)
- {
- list.RemoveAt();
- }
- });
- 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。
编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]的更多相关文章
- 编写高质量代码改善C#程序的157个建议[1-3]
原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...
- 读书--编写高质量代码 改善C#程序的157个建议
最近读了陆敏技写的一本书<<编写高质量代码 改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...
- 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试
建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...
- 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本
建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...
- 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码
建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...
- 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣
建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...
- 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释
建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...
- 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释
建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...
- 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量
建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...
随机推荐
- linux 下安装nodejs,CentOS 6.5 系统
本文采用nodejs 通过源码编译安装方式 编译需要gcc-c++编译器和openssl-devel库的支持,如果没有需要先安装. 在centos下可以先执行:yum install gcc-c++ ...
- 《精解Windows 10》
<精解Windows 10>全面深入讲解Windows 10操作系统的使用方法.本书共计14章内容.第一章简述Windows 10操作系统的一些变革:第二章介绍Modern 2.0界面的体 ...
- multiprocessing module in python(转)
序.multiprocessing python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程.Python提供了非常好用的多进程包mu ...
- 动手学习TCP: 环境搭建
前一段时间通过Wireshark抓包,定位了一个客户端和服务器之间数据传输的问题.最近就抽空看了看<TCP/IP详解 卷1>中关于TCP的部分,书中用了很多例子展示了TCP/IP协议中的一 ...
- JMS介绍入门大白话版
以下内容转自: http://setting.iteye.com/blog/1097767 ------------------------------------------------------ ...
- codeforces 422A A. Borya and Hanabi(暴力)
题目链接: A. Borya and Hanabi time limit per test 2 seconds memory limit per test 256 megabytes input st ...
- POJ 1066 Treasure Hunt --几何,线段相交
题意: 正方形的房子,给一些墙,墙在区域内是封闭的,给你人的坐标,每穿过一道墙需要一把钥匙,问走出正方形需要多少把钥匙. 解法: 因为墙是封闭的,所以绕路也不会减少通过的墙的个数,还不如不绕路走直线, ...
- Topcoder SRM 619 DIv2 500 --又是耻辱的一题
这题明明是一个简单的类似约瑟夫环的问题,但是由于细节问题迟迟不能得到正确结果,结果比赛完几分钟才改对..耻辱. 代码: #include <iostream> #include <c ...
- 转: __asm__ __volatile__内嵌汇编用法简述
from: http://www.embedu.org/Column/Column28.htm __asm__ __volatile__内嵌汇编用法简述 作者:刘老师,华清远见嵌入式学院高级讲师,AR ...
- smarty3缓存设置