SlideShare une entreprise Scribd logo
1  sur  18
浅析延迟执行 实现,优点,陷阱以及题外话
背景 假设一个场景: 我们需要获取并遍历一个包含大量元素的序列,从中找出我们需要的某个元素。 在此为了简单起见,我们假设该序列包含1000万个int值,我们需要找到的是100万这个值。
第一类实现方式:使用已有类型 要从一个序列中过滤出一个值则首先需要生成这个序列。在此我们随意挑选三个序列类型: int[] Collection<int> List<int>
第二种方式:延迟执行 要实现延迟执行(Deferred Execution)有两种方式可选: 自己创建实现了IEnumerable<T>接口的类型 使用yield关键字 这两种方式具体是如何实现延迟执行的?我们稍后根据代码讲解。
实现(1) 第一种方式中的三个已有类型的实现都相当简单,请看代码。 生成数组的代码: 生成Collection<int>的代码:
实现(1) 生成List<int>的代码:
实现(2) 自己创建实现了IEnumerable<T>的类型的方式是最复杂,最难写的。这个方式的基本思路就是构造一个状态机,由于要在多个状态之间做切换,所以很容易出错。请看代码: 由于代码过长,在此只给出代码结构图。这个类型同时实现了IEnumerable<T>和IEnumerator<T>两个接口。其MoveNext方法和Current属性每被调用一次时,才即时生成一个元素,这样就避免了一次性填充整个序列,从而实现了延迟执行。
实现(3) 使用yield关键字是最简单,最偷懒的方式。 实际上,yield背后对应的实现和我们讲到的上一种方式基本是一样的。编译器会把包含yield的代码块构造成一个同时实现了IEnumerable<T>和IEnumerator<T>的类型。
测试(1) 测试中有几个辅助方法(TestTime,IterateSequence和TestSpeed)需要简单说明,请看代码: TestTime的代码: TestTime接受一个Action类型的参数,在方法体内执行action并为其计时,最后输出所耗时间。
测试(1) IterateSequence的代码: 这段代码很简单:迭代一个序列,当找到要找的值之后则break出去。
测试(1) TestSpeed的代码: 这段代码测试所有五种实现方式的效率。把创建序列和过滤序列的代码包裹在lambda表达式中传入TestTime。
测试(2) 从1000万个元素中筛选 从1亿个元素中筛选 可以发现,当序列中元素数量增加时,前三种实现方式的耗时量都在呈线性增长。 而后两种实现方式的耗时量则基本没有变化。
总结(1):延迟执行的好处 从前面的测试结果中可以看出,延迟执行的最明显的优势即在于不会立即创建整个序列,而是在调用方索取时才即时生成元素。 这也正好解释了为什么将序列容量从1000万增加为1亿时延迟执行的方式执行时间基本不变。因为延迟执行的方法总是只生成100万个元素而已。
总结(2):可能的陷阱 由于用来生成序列的算法被封装在了状态机内,所以每次用foreach迭代这个序列时,整个序列都会被重新生成一次。 如果需要避免这种行为,可以通过在延迟执行的返回结果上调用ToArray()或ToList()。然后在每次迭代中都使用已经填充好的Array或List。 其实这种特性在有的场景下是很有益的,比如生成序列的算法依赖于某些外部的变化条件(数据库,网络数据或者系统时间)。
题外话(1):foreach 我们每天都会用到的foreach究竟是如何实现的呢? 可以看出,一个foreach的“空转”循环基本等价于一个while循环加一个try/finally代码块。 请注意在finally代码块中调用了Dispose方法。如果这个foreach作用于一个延迟执行方法的返回值上,那么对Dispose的调用就相当于把状态机的状态清零。
题外话(2):序列的重新生成 前面讲foreach的题外话其实是为了讲解序列的重新生成做基础。 前面已经讲过,foreach的尾部会调用迭代器的Dispose方法,把状态机的状态清零。这样,如果有下一个foreach来迭代同一个序列的话,则会将封装在状态机内的生成元素的算法重新执行一遍,也就相当于重新生成了整个序列。 这样说或许过于晦涩,请看下一页的图解。
题外话(2):序列的重新生成 请看这把春田步枪,你装入子弹(调用GetEnumerator),撞针顶住了第一颗子弹(第一次调用MoveNext),开枪(访问Current属性),然后撞针顶住下一颗子弹(又一次调用MoveNext),反复开枪(反复调用MoveNext并访问Current属性),直到子弹耗尽(MoveNext返回了false),枪膛打开了(调用了Dispose)。然后再装入子弹开始下一轮的射击(序列的重新生成)。
谢谢

Contenu connexe

Tendances

12, string
12, string12, string
12, stringted-xu
 
C python 原始碼解析 投影片
C python 原始碼解析 投影片C python 原始碼解析 投影片
C python 原始碼解析 投影片kao kuo-tung
 
C++基礎程式設計 ch5 陣列
C++基礎程式設計 ch5 陣列C++基礎程式設計 ch5 陣列
C++基礎程式設計 ch5 陣列requiemformemories
 
C++基礎程式設計 ch2 變數與運算子
C++基礎程式設計 ch2 變數與運算子C++基礎程式設計 ch2 變數與運算子
C++基礎程式設計 ch2 變數與運算子requiemformemories
 
Swift 程序语言介绍
Swift 程序语言介绍Swift 程序语言介绍
Swift 程序语言介绍明 李
 
C++基礎程式設計 ch3 條件判斷
C++基礎程式設計 ch3 條件判斷C++基礎程式設計 ch3 條件判斷
C++基礎程式設計 ch3 條件判斷requiemformemories
 
Lesson 2 Basicstructure
Lesson 2 BasicstructureLesson 2 Basicstructure
Lesson 2 BasicstructureRyan Chung
 
Javascript 培训第三节 基础下
Javascript 培训第三节 基础下Javascript 培训第三节 基础下
Javascript 培训第三节 基础下liziqi7
 
Javascript oop-o52tiger
Javascript oop-o52tigerJavascript oop-o52tiger
Javascript oop-o52tigero52tiger
 

Tendances (12)

12, string
12, string12, string
12, string
 
關於Import
關於Import關於Import
關於Import
 
C python 原始碼解析 投影片
C python 原始碼解析 投影片C python 原始碼解析 投影片
C python 原始碼解析 投影片
 
C++基礎程式設計 ch5 陣列
C++基礎程式設計 ch5 陣列C++基礎程式設計 ch5 陣列
C++基礎程式設計 ch5 陣列
 
C++基礎程式設計 ch2 變數與運算子
C++基礎程式設計 ch2 變數與運算子C++基礎程式設計 ch2 變數與運算子
C++基礎程式設計 ch2 變數與運算子
 
Swift 程序语言介绍
Swift 程序语言介绍Swift 程序语言介绍
Swift 程序语言介绍
 
C++基礎程式設計 ch3 條件判斷
C++基礎程式設計 ch3 條件判斷C++基礎程式設計 ch3 條件判斷
C++基礎程式設計 ch3 條件判斷
 
Lesson 2 Basicstructure
Lesson 2 BasicstructureLesson 2 Basicstructure
Lesson 2 Basicstructure
 
C語言結構與串列
C語言結構與串列 C語言結構與串列
C語言結構與串列
 
Javascript 培训第三节 基础下
Javascript 培训第三节 基础下Javascript 培训第三节 基础下
Javascript 培训第三节 基础下
 
SCJP ch18
SCJP ch18SCJP ch18
SCJP ch18
 
Javascript oop-o52tiger
Javascript oop-o52tigerJavascript oop-o52tiger
Javascript oop-o52tiger
 

En vedette

Web 2.0 vodcast
Web 2.0 vodcastWeb 2.0 vodcast
Web 2.0 vodcastonebel
 
Khn werkconf-duurzamebinnenstad-120124
Khn werkconf-duurzamebinnenstad-120124Khn werkconf-duurzamebinnenstad-120124
Khn werkconf-duurzamebinnenstad-120124Hermannus Stegeman
 
Pragmatic+unit+testing+in+c%23+with+n unit%2 c+second+edition
Pragmatic+unit+testing+in+c%23+with+n unit%2 c+second+editionPragmatic+unit+testing+in+c%23+with+n unit%2 c+second+edition
Pragmatic+unit+testing+in+c%23+with+n unit%2 c+second+editioncuipengfei
 
3 Things Every Sales Team Needs to Be Thinking About in 2017
3 Things Every Sales Team Needs to Be Thinking About in 20173 Things Every Sales Team Needs to Be Thinking About in 2017
3 Things Every Sales Team Needs to Be Thinking About in 2017Drift
 
How to Become a Thought Leader in Your Niche
How to Become a Thought Leader in Your NicheHow to Become a Thought Leader in Your Niche
How to Become a Thought Leader in Your NicheLeslie Samuel
 

En vedette (6)

Texel 120115
Texel 120115Texel 120115
Texel 120115
 
Web 2.0 vodcast
Web 2.0 vodcastWeb 2.0 vodcast
Web 2.0 vodcast
 
Khn werkconf-duurzamebinnenstad-120124
Khn werkconf-duurzamebinnenstad-120124Khn werkconf-duurzamebinnenstad-120124
Khn werkconf-duurzamebinnenstad-120124
 
Pragmatic+unit+testing+in+c%23+with+n unit%2 c+second+edition
Pragmatic+unit+testing+in+c%23+with+n unit%2 c+second+editionPragmatic+unit+testing+in+c%23+with+n unit%2 c+second+edition
Pragmatic+unit+testing+in+c%23+with+n unit%2 c+second+edition
 
3 Things Every Sales Team Needs to Be Thinking About in 2017
3 Things Every Sales Team Needs to Be Thinking About in 20173 Things Every Sales Team Needs to Be Thinking About in 2017
3 Things Every Sales Team Needs to Be Thinking About in 2017
 
How to Become a Thought Leader in Your Niche
How to Become a Thought Leader in Your NicheHow to Become a Thought Leader in Your Niche
How to Become a Thought Leader in Your Niche
 

Deferred execution