前言

    软件开发过程中,不可避免会用到集合,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个建议[泛型集合、选择集合、集合的安全]的更多相关文章

  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 ...

随机推荐

  1. Set a static file on django

    1. In setting file: ROOT_PATH='/home/ronglian/project/taskschedule' STATIC_URL = '/static/' STATICFI ...

  2. 如何对ZBrush中面部进行快速布线

    面部布线的最重要目的是为了表情动画.人物内心的各种不同的心里活动,主要是通过面部表情反映出来.而面部变化最丰富的地方是眼部(眉毛)和口 部,其他部位则相应的会受这两部分的影响而变化.对于面部表情,必须 ...

  3. HDU 3333 Turing Tree --树状数组+离线处理

    题意:统计一段序列[L,R]的和,重复元素只算一次. 解法:容易看出在线做很难处理重复的情况,干脆全部讲查询读进来,然后将查询根据右端点排个序,然后离散化数据以后就可以操作了. 每次读入一个数,如果这 ...

  4. HDU 1556 Color the ball

    这题用线段树的话简直就是一个水题..不过刚学树状数组,要用一下. 题意:每次给你a,b,表明a~b之间涂色,然后最后一次输出每个气球被涂色的次数. 要用树状数组就要考虑怎么转化为前缀和问题,这题可以这 ...

  5. CSU 1081 集训队分组

    题意:有n个学生,比了一场比赛,但是榜单看不到了.现在告诉你m段信息,每段信息的内容是(a,b),表示a的排名比b的高.问你能不能根据这些信息得出这场比赛的前k名. 思路:用拓扑排序找出一组符合k个人 ...

  6. 权限框架 - shiro 授权demo

    之前说了权限认证,其实也就是登录验证身份 这次来说说shiro的授权 shiro可以针对角色授权,或者访问资源授权 两者都行,但是在如今的复杂系统中,当然使用后者,如果你是小系统或者私活的话,前者即可 ...

  7. nginx + fastDFS 设置开机自动启动

    由于在服务器上有太多的软件 不可能每次启动都要重新启动服务吧(每晚断电...必须重启电脑) vim /etc/rc.d/rc.local 添加下列脚本 /usr/bin/fdfs_trackerd / ...

  8. 为Unity项目生成文档(一)

    VS生成chm帮助文档 VS代码中使用Xml注释,并通过Sandcastle生成chm文档的文章,这几篇值得分享: 使用.NET中的XML注释(一) -- XML注释标签讲解 使用.NET中的XML注 ...

  9. 常用excel技巧

    1.excel 设置行列分色显示  =MOD(ROW(),2)=0 2.多表匹配数据 通过身份证在另外一个表查找这个人的基本信息 第一张表 第二张表: =VLOOKUP(F12,'2014总表'!D: ...

  10. WPF Extended WPF Toolkit

    1.VS 2013 通过NUGet获取Extended WPF Toolkit 我自己的项目已安装 2.在自己页面引用Extended WPF Toolkit xmlns:xctk="htt ...