C#程序编写高质量代码改善的157个建议【20-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();
}
C#程序编写高质量代码改善的157个建议【20-22】[泛型集合、选择集合、集合的安全]的更多相关文章
- C#程序编写高质量代码改善的157个建议【16-19】[动态数组、循环遍历、对象集合初始化]
前言 软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类.不管是数组还是集合类,它们都有各自的优缺点.如何使用好集合是我们在开发过程中必须掌握的技巧.不要小看这些技巧,一旦在开 ...
- C#程序编写高质量代码改善的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]
前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理解的东西,有些地方可能理解的不太到位,还望指正. 建议1. ...
- C#程序编写高质量代码改善的157个建议【13-15】[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]
前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深 ...
- C#程序编写高质量代码改善的157个建议【10-12】[创建对象时需要考虑是否实现比较器、区别对待==和Equals]
前言 建议10.创建对象时需要考虑是否实现比较器 建议11.区别对待==和Equals 建议12.重写Equals时也要重写GetHashCode 建议10.创建对象时需要考虑是否实现比较器 有对象的 ...
- C#程序编写高质量代码改善的157个建议【4-9】[TryParse比Parse、使用int?来确保值类型也可以为null、readonly和const、0值设为枚举的默认值、避免给枚举类型的元素提供显式的值、习惯重载运算符]
建议4.TryParse比Parse好 如果注意观察,除string之外的所有的基元类型.会发现它们都有两个将字符串转换为自身类型的方法:Parse和TryParse.以类型double为例. 两者最 ...
- 博友的 编写高质量代码 改善java程序的151个建议
编写高质量代码 改善java程序的151个建议 http://www.cnblogs.com/selene/category/876189.html
- 编写高质量代码--改善python程序的建议(六)
原文发表在我的博客主页,转载请注明出处! 建议二十八:区别对待可变对象和不可变对象 python中一切皆对象,每一个对象都有一个唯一的标识符(id()).类型(type())以及值,对象根据其值能否修 ...
- 编写高质量代码--改善python程序的建议(八)
原文发表在我的博客主页,转载请注明出处! 建议四十一:一般情况下使用ElementTree解析XML python中解析XML文件最广为人知的两个模块是xml.dom.minidom和xml.sax, ...
- 编写高质量代码改善java程序的151个建议——导航开篇
2014-05-16 09:08 by Jeff Li 前言 系列文章:[传送门] 下个星期度过这几天的奋战,会抓紧java的进阶学习.听过一句话,大哥说过,你一个月前的代码去看下,慘不忍睹是吧.确实 ...
随机推荐
- JavaScript笔记六
1.对象(Object) - 对象是JS中的引用数据类型 - 对象是一种复合数据类型,在对象中可以保存多个不同数据类型的属性 - 使用typeof检查一个对象时,会返回object - 创建对象 - ...
- Xftp工具连接 报错: 无法与 “ 目标IP ” 连接
这里是OpenSSH升级导致 查看sshd_config [root@sdw1 glibc]# vim /etc/ssh/sshd_config 查找后进行修改 [root@sdw1 glibc]# ...
- SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".错误的解决方法
1.今天新git下来的项目报错如下: SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".SLF4J: ...
- 面试阿里被“吊打”,一问Spring三不知,半年后二战终拿下offer
Spring框架是一个为Java应用程序的开发提供了综合.广泛的基础性支持的Java平台.Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发. 近两年来,许多大厂在面 ...
- 英语口语考试资料Family
I Love my family 12 years ago, I was born in a happy family, there was a gentle father, a beautif ...
- Mysql多表关系
mysql多表关系 多表关系是关系型数据库特有的 三种关系 一对一关系 一对多关系 多对多关系 总结 一对一 例子:用户和用户信息 外键设置在用户上,外键字段唯一非空 添加 无级联:先增加被关联表记录 ...
- 利用tp5开发智慧软文发布系统中遇到的一些坑
1. PHP 计算两个时间戳之间相差的时间 假设你两个时间戳为$a,$b; 你可以用$c=$a-$b;(反正就是大的减小的),这时$c就是两个时间间隔的秒数了. 想求两个时间间隔的天数就用:$c/(6 ...
- Python网络爬虫——BeautifulSoup4库的使用
使用requests库获取html页面并将其转换成字符串之后,需要进一步解析html页面格式,提取有用信息. BeautifulSoup4库,也被成为bs4库(后皆采用简写)用于解析和处理html和x ...
- .Net Core的API网关Ocelot的使用(二)[负载,限流,熔断,Header转换]
网关的负载均衡 当下游拥有多个节点的时候,我们可以用DownstreamHostAndPorts来配置 { "UpstreamPathTemplate": "/Api_A ...
- 使用python实现http服务器
主要使用python实现了一个http服务器.http服务器实现了用户的注册和登录的简单功能,当然还可以继续扩展. 数据的存储使用的是文件,有兴趣的话可以使用数据库进行存储.当然根据个人兴趣而定. 本 ...