我有以下代码,我认为它会起作用:
static int ComputerFailsOnTrue(bool flag)
{
if (flag)
throw new Exception();
return 10; // not relevant
}
static IEnumerable<double> StartComputer()
{
yield return ComputerFailsOnTrue(true);
yield return ComputerFailsOnTrue(false);
yield return ComputerFailsOnTrue(false);
}
static public void Main()
{
foreach (var item in StartComputer().Skip(1))
{
Console.WriteLine($"Hey {item}");
}
Console.ReadLine();
}
但它失败了(我会得到异常),因为集合的第一个元素将被计算。为什么给定集合中枚举器的 moveNext
方法总是计算当前元素?
是否假设当前的计算可以依赖于先前的状态?
最佳答案
这并不是 LINQ 本身的失败。这是一个与两件事相关的问题 - IEnumerator<T>
interface和 C# 的 iterator methods .
IEnumerator<T>
只有两个有趣的成员 - MoveNext
和 Current
.执行Skip
的唯一方法在这样的接口(interface)之上1 是调用MoveNext
与您希望跳过的项目数一样多的次数,当然还有 MoveNext
的任何实现可以在每次方法调用时自由运行任意代码。它能做的是避免访问 Current
.
在 C# 的迭代器实现中,它“自动”生成 IEnumerable<T>
的实现。和 IEnumerator<T>
, MoveNext
和 Current
密切相关 - 你只写一个方法,每次该方法获得控制权(对于 MoveNext
),它还必须计算下一个 Current
值(value)。
如果您正在实现 IEnumerator<T>
手动,你可以自由地在你的 MoveNext
中放置一些逻辑Current
中的方法和一些逻辑属性(property),包括评估Current
懒惰的。在这样的实现中,如果调用 compute(true)
是您实现 Current
的一部分,您的代码将按预期工作。
1LINQ 中可能有一些专门化,可以绕过对内置集合类使用枚举器,但一般来说,这是使用的接口(interface)。
关于c# - 为什么 linq 不实现完全懒惰?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41636796/