[我正在研究iterator方法的内部机制,并且我注意到通过迭代器获得的IEnumerator<T>
与通过LINQ方法获得的IEnumerator<T>
在行为上存在奇怪的区别。如果枚举期间发生异常,则:
示例。 IEnumerator<T>
会被顽固地枚举,直到完成:
IEnumerator<int>
让我们尝试枚举在第三个项目上抛出的LINQ枚举器:
private static void StubbornEnumeration(IEnumerator<int> enumerator)
{
using (enumerator)
{
while (true)
{
try
{
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
Console.WriteLine("Finished");
return;
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
}
}
}
输出:
12例外:糟糕!45例外:糟糕!78例外:糟糕!10完成
现在让我们对在第三个项目上抛出的迭代器进行相同的尝试:
var linqEnumerable = Enumerable.Range(1, 10).Select(i =>
{
if (i % 3 == 0) throw new Exception("Oops!");
return i;
});
StubbornEnumeration(linqEnumerable.GetEnumerator());
输出:
12例外:糟糕!完成
我的问题是:这种不一致的原因是什么?哪种行为对实际应用更有用?
注:此观察是在另一个与迭代器相关的问题中的StubbornEnumeration(MyIterator().GetEnumerator());
static IEnumerable<int> MyIterator()
{
for (int i = 1; i <= 10; i++)
{
if (i % 3 == 0) throw new Exception("Oops!");
yield return i;
}
}
之后进行的。
更新:我做了一些观察。并非所有LINQ方法的行为都相同。例如,answer by Dennis1679方法在内部作为.NET Framework上的Take
实现,因此它的行为就像一个迭代器(异常立即完成)。但是在.NET Core上,它的实现方式可能有所不同,因为在例外情况下,它会继续运行。