前言

    软件开发过程中,不可避免会用到集合,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. mysql force index() 强制索引的使用

    mysql force index() 强制索引的使用 之前跑了一个SQL,由于其中一个表的数据量比较大,而在条件中有破坏索引或使用了很多其他索引,就会使得sql跑的非常慢... 那我们怎么解决呢? ...

  2. A python tool to static sim.log duration time

    When working ALU IMS Patch team, we need to static the SU duration to add it to the patch report, th ...

  3. fcntl 获取文件状态标志

    int fcntl(int fd,int cmd,...) 函数fcntl提供了非常丰富的功能.主要依赖于cmd的各种参数: 复制已有的文件描述符 F_DUPFD,F_DUPFD_CLOEXEC 获取 ...

  4. [嵌入式开发板]iTOP-4412开发板linux 系统存储空间的修改

    平台:iTOP-4412开发板 这里我们以修改成 1G 存储空间为例来讲解修改方法, 如果需要改 成其他大小的存储空间,参照此方法修改即可. 首先连接好 iTOP-4412 开发板的调试串口到 pc  ...

  5. 使用LVS实现负载平衡之Windows Server 2008配置

    LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统.本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一.承载于 II ...

  6. POJ 1703 Find them,Catch them ----种类并查集(经典)

    http://blog.csdn.net/freezhanacmore/article/details/8774033?reload  这篇讲解非常好,我也是受这篇文章的启发才做出来的. 代码: #i ...

  7. uGUI练习(七) Drag And Drop

    练习目标 练习UI的拖放操作 一.相关组件 EventTrigger Canvas Group ScrollRect Mask Scrollbar 二.拖放练习 1.创建一个Panel,命名Panel ...

  8. Mac下Android Studio中获取SHA1和MD5

    有很多人讲这个的时候,老是只把这个代码标出来又不说为什么 keytool -list -keystore debug.keystore keytool   这个是java的 jdk中一个工具(做签名文 ...

  9. 常用excel技巧

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

  10. scroll滚动条插件初始化问题

    一种特殊场景下是滚动条容器先隐藏,点击某个东西后显示出来.然后实例化滚动条.实例 js: var flag = true; document.getElementById('btn1').onclic ...