yield return 方法在不应该的时候被优化掉了

yield return method optimized away when it shouldn't be

我偶然发现了我的库的一个测试问题,其中 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.

SampleForEachIEnumerable 的初始化被优化掉了,作为副作用 Main 里面的 item 永远不会改变。

为什么 Sample 没有在 ForEach 迭代?

真的 需要在 After 迭代 items 吗?

如果是,为什么优化如此之深以至于 ForEach 处的迭代也会停止直到被激发,从而使输出 -1 然后 9 也成为可能?

IEnumerable<T> 对象具有延迟执行,这意味着它们在访问以进行迭代时执行。当我们开始将它的对象迭代或具体化为 List<T>Array 时,它实际上被调用了。

如果您将 ToList() 调用添加到您的 ForEach 方法,您将看到预期的输出:

After(ForEach(Sample(10), v => item = v).ToList(), () => Console.WriteLine(item));

如您所见,您实现了 ForEach() 方法的结果 IEnumerable 它也会调用 Sample() 并执行。

您可以像下面这样修改您的方法,以查看实际调用该方法的时间:

static IEnumerable<int> Sample(int count)
{
    Console.WriteLine("Sample Invoked");
    for (int i = 0; i < count; i++)
        yield return i;
}

static IEnumerable<int> ForEach(IEnumerable<int> items, Action<int> action)
{
    Console.WriteLine("ForEach Invoked:");
    foreach (int item in items)
    {
        action(item);
        yield return item;
    }
}

然后这样调用:

int item = -1;
After(ForEach(Sample(10), v => item = v), () => Console.WriteLine(item));
Console.WriteLine(item);
    
After(ForEach(Sample(10), v => item = v).ToList(), () => 
Console.WriteLine(item));
Console.WriteLine(item);

输出为:

-1

-1

ForEach Invoked:

Sample Invoked

9

9

查看 Fiddle DEMO 以观察它的运行情况。