收益回报方法在不应该使用时被优化

时间:2018-12-11 11:39:21

标签: c# ienumerable compiler-optimization yield-return

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

IEnumerableSampleForEach的初始化已被优化,并且item内的副作用Main从未改变。

为什么没有SampleForEach上进行迭代?

我真的需要在items上迭代After吗?

如果是,为什么优化如此之深,甚至导致ForEach处的迭代都被暂停,直到被激发,使得输出-1然后9也是可能的?

1 个答案:

答案 0 :(得分:2)

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

     

每次调用:

     

调用的样本

     

9

     

9

请参阅Fiddle DEMO以观察它的作用。