【发布时间】:2019-05-12 09:35:02
【问题描述】:
我偶然发现了我的库的一个测试问题,其中 yield return 因我不理解的原因而被优化。
概念证明代码:
static IEnumerable<int> Sample(int count)
{
for (int i = 0; i < count; i++) yield return i;
}
static IEnumerable<int> ForEach(IEnumerable<int> items, Action<int> action)
{
foreach (int item in items) { action(item); yield return item; }
}
static void After(IEnumerable<int> items, Action action)
{
action();
}
static void Main(string[] args)
{
int item = -1;
After(ForEach(Sample(10), v => item = v), () => Console.WriteLine(item));
Console.WriteLine(item);
Console.ReadKey();
}
我希望输出是9,然后是9。
实际输出是-1,然后是-1。
IEnumerables 在Sample 和ForEach 的初始化被优化掉了,作为副作用item 内部Main 永远不会改变。
为什么Sample 没有被迭代到ForEach?
我真的需要在After 迭代items 吗?
如果是,为什么优化如此之深,以至于即使在ForEach 的迭代也被暂停直到被激发,从而使输出-1 然后9 也成为可能?
【问题讨论】:
-
如果你不迭代项目,
ForEach方法将永远不会被调用。例如,您可以使用ForEach(...).ToList()。 -
您的
after方法永远不会调用items.GetEnumerator。请记住,yield return是惰性评估。 -
不相关,但有一个内置的
Enumerable.Range方法用于生成简单序列。 -
Dai,阅读第一个问题 - 为什么
Sample没有被迭代到ForEach? -
@Luka967 因为
ForEach方法也是一个惰性求值状态机函数。只需使用yield return,编译器就会重写您的方法以进行延迟评估,其中包括after和您的ForEach。
标签: c# ienumerable compiler-optimization yield-return