c# - 为什么 linq 不实现完全懒惰?

标签 c# linq

我有以下代码,我认为它会起作用:

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>只有两个有趣的成员 - MoveNextCurrent .执行Skip的唯一方法在这样的接口(interface)之上1 是调用MoveNext与您希望跳过的项目数一样多的次数,当然还有 MoveNext 的任何实现可以在每次方法调用时自由运行任意代码。它能做的是避免访问 Current .

在 C# 的迭代器实现中,它“自动”生成 IEnumerable<T> 的实现。和 IEnumerator<T> , MoveNextCurrent密切相关 - 你只写一个方法,每次该方法获得控制权(对于 MoveNext ),它还必须计算下一个 Current值(value)。

如果您正在实现 IEnumerator<T>手动,你可以自由地在你的 MoveNext 中放置一些逻辑Current 中的方法和一些逻辑属性(property),包括评估Current懒惰的。在这样的实现中,如果调用 compute(true)是您实现 Current 的一部分,您的代码将按预期工作。


1LINQ 中可能有一些专门化,可以绕过对内置集合类使用枚举器,但一般来说,这是使用的接口(interface)。

关于c# - 为什么 linq 不实现完全懒惰?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41636796/

相关文章:

c# - wpf 绑定(bind) Material 设计对话框宽度小于或超过窗口宽度

c# - IEnumerable<T>.Intersect<T> 中的 Type 参数有什么作用?

c# - 如何在动态选择中使用表达式创建 "inline if statement"以进行空检查

c# - 使用正则表达式连接 linq

c# - 将可观察的路径集合转换为文件名集合

c# - Azure 中的 ReSeed 数据库

c# - 验证有效时间的正则表达式

c# - C#中在语句外声明using语句的目标对象

c# - 使用动态 LINQ 展平一对多关系

c# - 如何将 Nullable<T> 参数与 nHibernate Linq 表达式一起使用?