gpt4 book ai didi

c# - 为什么 IEnumerator 会影响 IEnumerable 的状态,即使 ienumerator 从未到达终点?

转载 作者:可可西里 更新时间:2023-11-01 08:43:39 25 4
gpt4 key购买 nike

我很好奇为什么以下内容会在“最后”分配时抛出错误消息(文本阅读器关闭异常):

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() 时,文件实际上已打开。就个人而言,我认为这本身就是一个错误;我期望并希望它是惰性的 - 仅在您尝试开始迭代时才打开文件。
  • 当您第一次根据ReadLines 的返回值调用GetEnumerator() 时,它实际上会返回相同的引用。<
  • First() 调用 GetEnumerator() 时,它将创建一个克隆。这将与 textEnumerator
  • 共享相同的 StreamReader
  • 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;
}
}
}
}

在后面的代码中,每次调用 GetEnumerator() 时都会打开一个新的 StreamReader - 因此结果是 test.txt 中的每一对行。

关于c# - 为什么 IEnumerator<T> 会影响 IEnumerable<T> 的状态,即使 ienumerator 从未到达终点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14039571/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com