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
.
Sample
和 ForEach
处 IEnumerable
的初始化被优化掉了,作为副作用 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 以观察它的运行情况。
我偶然发现了我的库的一个测试问题,其中 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
.
Sample
和 ForEach
处 IEnumerable
的初始化被优化掉了,作为副作用 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 以观察它的运行情况。