我偶然发现了我的图书馆的一个测试问题,其中一个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
。
IEnumerable
和Sample
上ForEach
的初始化已被优化,并且item
内的副作用Main
从未改变。
为什么没有Sample
在ForEach
上进行迭代?
我真的需要在items
上迭代After
吗?
如果是,为什么优化如此之深,甚至导致ForEach
处的迭代都被暂停,直到被激发,使得输出-1
然后9
也是可能的?
答案 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以观察它的作用。