为什么IEumerator 会影响IEnumerable 的状态,即使枚举器从未到达终点?
我很好奇为什么以下内容在“最后”赋值时抛出错误消息(文本阅读器关闭exception):
IEnumerable textRows = File.ReadLines(sourceTextFileName); IEnumerator textEnumerator = textRows.GetEnumerator(); string first = textRows.First(); string last = textRows.Last();
但是以下执行正常:
IEnumerable textRows = File.ReadLines(sourceTextFileName); string first = textRows.First(); string last = textRows.Last(); IEnumerator textEnumerator = textRows.GetEnumerator();
不同行为的原因是什么?
据我所知,你在框架中发现了一个错误。 由于一些事物的相互作用,这是相当微妙的:
- 当您调用
ReadLines()
,实际上会打开该文件。 就个人而言,我认为这本身就是一个错误; 我希望并希望它是懒惰的 – 只有当你尝试开始迭代它时才打开文件。 - 当您第一次在
ReadLines
的返回值上调用GetEnumerator()
时,它实际上将返回相同的引用。 - 当
First()
调用GetEnumerator()
,它将创建一个克隆。 这将与textEnumerator
共享相同的StreamReader
- 当
First()
处理它的克隆时,它将处理StreamReader
,并将其变量设置为null
。 这不会影响原始变量,现在引用已经处理的StreamReader
- 当
Last()
调用GetEnumerator()
,它将创建原始对象的克隆,并配置StreamReader
。 然后它尝试从该读取器读取,并抛出exception。
现在将其与第二个版本进行比较:
- 当
First()
调用GetEnumerator()
,将返回原始引用,并使用打开的阅读器完成。 - 当
First()
然后调用Dispose()
,将释放阅读器并将变量设置为null
- 当
Last()
调用GetEnumerator()
,将创建一个克隆 – 但由于它的克隆值具有null
引用,因此创建了一个新的StreamReader
,因此它能够StreamReader
地读取该文件。 然后它处理克隆,关闭阅读器 - 当调用
GetEnumerator()
,原始对象的第二个克隆,打开另一个StreamReader
– 再次,没有问题。
所以基本上,第一个片段中的问题是你第二次调用GetEnumerator()
(在First()
)而没有丢弃第一个对象。
这是同一问题的另一个例子:
using System; using System.IO; using System.Linq; class Test { static void Main() { var lines = File.ReadLines("test.txt"); var query = from x in lines from y in lines select x + "/" + y; foreach (var line in query) { Console.WriteLine(line); } } }
你可以通过调用File.ReadLines
两次来解决这个问题 – 或者使用一个真正懒惰的ReadLines
实现,如下所示:
using System.IO; using System.Linq; class Test { static void Main() { var lines = ReadLines("test.txt"); var query = from x in lines from y in lines select x + "/" + y; foreach (var line in query) { Console.WriteLine(line); } } static IEnumerable ReadLines(string file) { using (var reader = File.OpenText(file)) { string line; while ((line = reader.ReadLine()) != null) { yield return line; } } } }
在后面的代码中,每次调用GetEnumerator()
时都会打开一个新的StreamReader
– 因此结果是test.txt中的每对行。