我很好奇为什么以下内容在“最后”赋值时抛出错误消息(文本阅读器关闭异常):
IEnumerable<string> textRows = File.ReadLines(sourceTextFileName);
IEnumerator<string> textEnumerator = textRows.GetEnumerator();
string first = textRows.First();
string last = textRows.Last();
但是以下执行正常:
IEnumerable<string> textRows = File.ReadLines(sourceTextFileName);
string first = textRows.First();
string last = textRows.Last();
IEnumerator<string> textEnumerator = textRows.GetEnumerator();
不同行为的原因是什么?
据我所知,你在框架中发现了一个错误。由于一些事物的相互作用,这是相当微妙的:
- 你打电话时
ReadLines()
,文件实际上是打开的。就个人而言,我认为这本身就是一个错误;我希望并希望它是懒惰的 - 只有当你尝试开始迭代它时才打开文件。
- 你打电话时
GetEnumerator()
该 第一 时间在返回值上 ReadLines
,它实际上会返回相同的引用。
- 什么时候
First()
电话 GetEnumerator()
,它将创建一个克隆。这将分享相同的 StreamReader
如 textEnumerator
- 什么时候
First()
处理它的克隆,它将处理它 StreamReader
,并设置 它的 变量到 null
。这不会影响原始变量,现在指的是已处置的变量 StreamReader
- 什么时候
Last()
电话 GetEnumerator()
,它将创建一个克隆 原始对象,配有配置 StreamReader
。然后它尝试从该读取器读取,并抛出异常。
现在将其与第二个版本进行比较:
- 什么时候
First()
电话 GetEnumerator()
,返回原始参考,完成开放阅读器。
- 什么时候
First()
然后打电话 Dispose()
,读者将被处理并且变量设置为 null
- 什么时候
Last()
电话 GetEnumerator()
,将创建一个克隆 - 但因为它克隆的值有一个 null
参考,一个新的 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<string> ReadLines(string file)
{
using (var reader = File.OpenText(file))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
}
在后面的代码中,一个新的 StreamReader
每次都打开 GetEnumerator()
被调用 - 所以结果是test.txt中的每对行。