我希望能够对我最近在调试器中完成的一个片段做一些澄清,但是根本无法理解。
我正在 PluralSight 上参加 C#课程,当前主题位于yield
,并返回带有关键字的IEnumerable<T>
。
我有这个过于基本的函数,它会返回IEnumerable
Vendors
的集合(一个包含Id
,CompanyName
和Email
的简单类:
public IEnumerable<Vendor> RetrieveWithIterator()
{
this.Retrieve(); // <-- I've got a breakpoint here
foreach(var vendor in _vendors)
{
Debug.WriteLine($"Vendor Id: {vendor.VendorId}");
yield return vendor;
}
}
我在单元测试中得到了这个代码,我用它来测试函数:
var vendorIterator = repository.RetrieveWithIterator(); // <-- Why don't it enter function?
foreach (var item in vendorIterator) // <-- But starts here?
{
Debug.WriteLine(item);
}
var actual = vendorIterator.ToList();
我真的无法理解,而且我确信很多初学者都遇到同样的问题,这就是为什么对RetrieveWithIterator
的初始调用没有启动该功能,而是开始当我们开始迭代它返回的IEnumerable
集合时(参见注释)。
答案 0 :(得分:12)
这称为延迟执行,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)) { ... }
迭代器块只在需要时运行;当枚举被迭代时,而不是之前,而不是之后。
答案 1 :(得分:2)
来自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();
当你编写一个实现延迟执行的方法时,你还必须决定是否使用延迟评估或急切评估来实现该方法。
延迟评估通常会产生更好的性能,因为它在整个集合评估过程中均匀分配开销处理,并最大限度地减少临时数据的使用。当然,对于某些操作,除了实现中间结果之外别无选择。
答案 2 :(得分:1)
当你在需要的时候循环它们时它会得到物品。这种方式说你只需要前4个结果,然后你就打破了,它不会产生更多的东西,你只是节省了一些处理能力!
来自MS Docs:
使用yield return语句一次返回一个元素。 您可以使用foreach语句或LINQ查询来使用迭代器方法。 foreach循环的每次迭代都会调用迭代器方法。在迭代器方法中达到yield return语句时,将返回表达式,并保留代码中的当前位置。下次调用迭代器函数时,将从该位置重新开始执行。 您可以使用yield break语句来结束迭代。
注意 - 如果你对产生的方法的结果.ToList()
,它将像返回单个列表一样工作,从而破坏了yield的目的。