在C#中理解产量问题
这个问题在这里已经有了答案:
这被称为延迟执行, yield
是懒惰的,只会按需要工作。
这具有很多优点,其中之一就是您可以创建看似无限的枚举:
public IEnumerable<int> InfiniteOnes()
{
while (true)
yield 1;
}
现在想象一下:
var infiniteOnes = InfiniteOnes();
会急切地执行,你会非常高兴地发现一个StackOverflow
异常。
另一方面,因为懒惰,你可以做到以下几点:
var infiniteOnes = InfiniteOnes();
//.... some code
foreach (var one in infiniteOnes.Take(100)) { ... }
然后,
foreach (var one in infiniteOnes.Take(10000)) { ... }
迭代器块将只在需要时运行; 当枚举被迭代时,不在之前,不在之后。
从msdn:
延期执行
延迟执行意味着对表达式的评估会延迟到实际需要实现的值。 当您必须处理大型数据集时,延迟执行可以大大提高性能,特别是在包含一系列链式查询或操作的程序中。 在最好的情况下,延迟执行只能通过源集合进行一次迭代。
在迭代器块中使用时,yield关键字(以yield-return语句的形式)直接在C#语言中支持延迟执行。 这样的迭代器必须返回IEnumerator
或IEnumerator<T>
类型的集合(或派生类型)。
var vendorIterator = repository.RetrieveWithIterator(); // <-- Lets deferred the execution
foreach (var item in vendorIterator) // <-- execute it because we need it
{
Debug.WriteLine(item);
}
var actual = vendorIterator.ToList();
渴望与懒惰评估
当你编写一个实现延迟执行的方法时,你还必须决定是否使用延迟评估或急切评估来实现该方法。
懒惰评估通常会产生更好的性能,因为它在整个集合评估过程中平均分配开销处理,并最大限度地减少临时数据的使用。 当然,对于一些操作来说,除了实现中间结果之外没有别的选择。
资源
当你在需要的时候循环它们会得到物品。 这样说你只需要前4个结果,然后你就破了,它不会再产生任何东西,你只是保存了一些处理能力!
来自MS Docs:
您使用yield return语句一次返回一个元素。 您可以使用foreach语句或LINQ查询来使用迭代器方法。 foreach循环的每次迭代调用迭代器方法。 在迭代器方法中达到yield return语句时,将返回expression,并保留代码中的当前位置。 下一次调用迭代器函数时,将从该位置重新启动执行。 您可以使用yield break语句结束迭代。
注意 - 如果您对.ToList()
方法产生结果,它将像您返回单个列表一样工作,从而破坏了yield的目的。