c# - 延迟执行和急切评估

标签 c# linq c#-3.0

你能给我一个在 C# 中使用急切求值的延迟执行的例子吗?

我从 MSDN 了解到,LINQ 中的延迟执行可以通过惰性求值或急切求值来实现。我可以在互联网上找到使用惰性求值的延迟执行的示例,但是我找不到任何使用急切求值的延迟执行的示例。

此外,延迟执行与惰性求值有何不同?在我看来,两者看起来都一样。您能否也为此提供任何示例?

最佳答案

下面是我的回答,但也请注意 Jon Skeet 今天在他的博客上谈到了一个事实,即他对 MSDN 中“懒惰”的含义并不完全满意,因为 MSDN 并不清楚懒惰的确切含义当他们在 Just how lazy are you ? 中使用它时他的帖子值得一读。

另外Wikipedia假设应该为惰性求值维护三个规则,并且在 MSDN 中不遵守第三点,这意味着如果再次调用 GetEnumerator,表达式将被求值不止一次(根据规范 Reset is not implemented on使用 yield 关键字生成的枚举器对象,目前大多数 linq 都使用它)


考虑一个函数

int Computation(int index)

立即执行

IEnumerable<int> GetComputation(int maxIndex)
{
    var result = new int[maxIndex];
    for(int i = 0; i < maxIndex; i++)
    {
        result[i] = Computation(i);
    }
    return result;
}
  • 调用函数时Computation被执行maxIndex
  • GetEnumerator 返回一个新的枚举器实例。
  • 每次调用 MoveNext 都会将存储在下一个数组单元格中的值放入 IEnumeratorCurrent 成员中,仅此而已。

成本:前期大,枚举期间小(仅副本)

延迟但急切的执行

IEnumerable<int> GetComputation(int maxIndex)
{
    var result = new int[maxIndex];
    for(int i = 0; i < maxIndex; i++)
    {
        result[i] = Computation(i);
    }
    foreach(var value in result)
    {
        yield return value;
    }
}
  • 当函数被调用时,自动生成的类(在规范中称为“可枚举对象”)实现 IEnumerable 的实例被创建,并且参数的副本(maxIndex) 存储在其中。
  • GetEnumerator 返回一个新的枚举器实例。
  • 第一次调用 MoveNext 执行计算方法的 maxIndex 次,将结果存储在数组中,Current 将返回第一个值。
  • MoveNext 的每次后续调用都会将存储在数组中的值放入 Current

成本:前期无成本,枚举开始时大,枚举期间小(仅副本)

延迟和惰性执行

IEnumerable<int> GetComputation(int maxIndex)
{
    for(int i = 0; i < maxIndex; i++)
    {
        yield return Computation(i);
    }
}
  • 当调用函数时,会发生惰性执行情况。
  • GetEnumerator 返回一个新的枚举器实例。
  • 每次调用 MoveNext 都会执行一次 Computation 代码,将值放入 Current 并让调用者立即对结果进行操作。

大多数linq使用延迟和惰性执行,但有些函数不能像排序那样。

成本:前期无成本,枚举期间适中(计算在那里执行)

总结

  • 立即意味着计算/执行在函数中完成,并在函数返回后完成。 (与大多数 C# 代码一样,完全 eager 评估)
  • 延期/Eager意味着大部分工作将在第一个 MoveNext 或创建 IEnumerator 实例时完成(对于 IEnumerable调用 GetEnumerator)
  • 延期/Lazy意味着每次调用 MoveNext 时都会完成工作,但之前不会。

Parallel LINQ它的做法略有不同,因为从调用者的角度来看,计算可以被认为是延迟的/惰性的,但在内部,一旦枚举开始,一些元素的计算就会并行开始。结果是,如果下一个值已经存在,您会立即获得它,否则您将不得不等待它。

关于c# - 延迟执行和急切评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2515796/

相关文章:

c# - 使用 Linq 将进程名称与 XML 列表进行比较

c# 将 linq 数据库记录转换为类实例

c# - 接口(interface)的好案例

C# 接口(interface) : Is it possible to refer to the type that implements the interface within the interface itself?

c# - DataTable 到 CSV 日期格式

c# - 使用文本框过滤列表框项目

c# - 使用 LINQ 对列表及其所有嵌套对象进行排序

c# - C#中的滑动窗口算法

wcf - 在发送请求之前需要帮助在我的 WCF 客户端中操作 SOAP header

c# - Entity Framework 4 使用外键在 WinForms 组合框中进行数据绑定(bind)