深入理解C#(十三)
*第六章(C# 2:实现迭代器的捷径)
知识简介
迭代器模式:行为模式的一种范例,允许访问一个数据项序列中的所有元素,而不用关心序列的类型。能有效构建一个数据管道。(数据项序列进入数据管道后,经过一系列不同的转换或过滤后再从另一端出来)
行为模式:一种简化对象之间通信的设计模式
迭代器模式通过IEnumerator和IEnumerable接口以及它们的泛型等价物来封装。若某个类型实现了IEnumerable接口,就意味着它可以被迭代访问。
难点在于怎么自建迭代器
C# 2:利用yield语句简化迭代器
利用C# 2和yield return来迭代实例集合:
1 | public IEnumerator GetEnumerator() |
这是一个实现迭代器块的方法,这个方法被声明为返回一个
IEnumerator接口,所以就只能使用迭代器块来实现返回类型为IEnumerable、IEnumerator等的方法。如果方法声明的返回类型是非泛型接口,那么迭代器块的生成类型(yield type)是object,否则就是泛型接口的类型参数。如:方法声明返回IEnumerable
在迭代器块中存在try/catch或try/finally代码块时,不能在try和finally中使用yield return
编写迭代器块代码实际上是在请求编译器创建一个状态机
当编译器看到迭代器块时,会为状态机创建一个嵌套类型,来正确记录块中的位置,以及局部变量(包括参数)的值。
这个状态机实现一下功能:
- 必须具有某个初始状态
- 每次调用MoveNext,提供下一个值之前(执行到yield return语句之前),执行GetEnumerator方法中的代码
- 使用Current属性时,返回生成的上一个值
- 知道何时完成生成值的操作,以便MoveNext返回false
迭代器的工作流程
- 在第一次调用MoveNext之前,CreateEnumerable不会被调用
- 所有工作在调用MoveNext时就完成了,获取Current的值,不执行任何代码
- 在yield return的位置,代码就停止执行,在下一次调用MoveNext时又继续执行
- 在一个方法中的不同地方可以编写多个yield return语句
- 代码不会在最后的yield return处结束,而是通过返回false的MoveNext调用来结束
进一步了解
yield return语句临时推出了方法,知道再次调用MoveNext后继续执行,根本没有检查finally代码块的行为。
迭代器块不能实现具有ref或out参数的方法
yield break结束迭代器的执行,类似于普通方法中的return语句
finally在迭代器块中常用于释放资源,通常与using语句配合使用
迭代器示例
迭代时刻表中的日期
C# 1
1 | for(DataTime day=timetable.StartDate; |
C# 2
foreach(DateTime day in timetable.DateRange)
想要手动实现迭代器很麻烦,但用迭代器块就很方便。为表示时刻表的类添加一个属性:
1 | public IEnumerable<DateTime> dateRange |
迭代文件中的行
在.NET 4中,框架提供了reader.ReadLines来实现,如何自己轻松实现?
C# 1
1 | using(TextReader reader=File.OpenText(filename)) |
- 获取TextReader
- 管理TextReader的生命周期(using)
- 迭代TextReader.ReadLine返回的行
- 对这些行处理
生命周期管理和迭代机制都是样板代码,有两种方法改进。
一。使用委托,编写一个工具方法,将阅读器和委托作为参数,为文件中的每一行调用该委托,最后关闭阅读器。
二。使用迭代器一次返回文件中的一行,如下
1 | public static IEnumerable<string> ReadLines(string filename) |
进一步改进:如果我们想从网络流中读取文本或使用UTF-8以外的编码格式,最简单的想法是重新修改方法签名,使其接受一个TextReader。但这个方案很糟糕,问题是,如果在第一次调用MoveNext()之前发生了异常,就没有机会清理了。另外,若GetEnumerate人()被调用两次,但它们使用相同的阅读器。
1 | static IEnumerable<String> ReadLines(Func <TextReader> provider) |
使用迭代器块和谓词对进行延迟过滤
看过linq后回来重看
CCR实现伪同步代码
CCR(Concurrency and Coordination Runtime,并发和协调运行时)
看完异步开发回来看