无法理解C#中的产量

geo*_*ker 11 .net c# yield yield-return

我希望能够对我最近在调试器中完成的一个片段做一些澄清,但是根本无法理解.

我正在PluralSight学习C#课程,目前的主题是关于并使用关键字返回a .yieldIEnumerable<T>

我有这个过于基本的函数,它返回一个(一个带有的简单类,和)的IEnumerable集合:VendorsIdCompanyNameEmail

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我在单元测试中得到了这个代码,我用它来测试函数:

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();
Run Code Online (Sandbox Code Playgroud)

我真的无法理解,我确信很多初学者都遇到了同样的问题,这就是为什么初始调用RetrieveWithIterator不会启动函数,而是在我们开始迭代其返回的IEnumerable集合时开始(见评论).

InB*_*een 14

这称为延迟执行,yield是懒惰的,只能按需要运行.

这有很多优点,其中之一就是你可以创建看似无限的枚举:

public IEnumerable<int> InfiniteOnes()
{
     while (true)
         yield 1;
}
Run Code Online (Sandbox Code Playgroud)

现在想象如下:

var infiniteOnes = InfiniteOnes();
Run Code Online (Sandbox Code Playgroud)

会急切地执行,你会有一个StackOverflow非常愉快的例外.

另一方面,因为它的懒惰,你可以做到以下几点:

var infiniteOnes = InfiniteOnes();
//.... some code
foreach (var one in infiniteOnes.Take(100)) { ... }
Run Code Online (Sandbox Code Playgroud)

然后,

foreach (var one in infiniteOnes.Take(10000)) { ... }
Run Code Online (Sandbox Code Playgroud)

迭代器块只在需要时运行; 当枚举被迭代时,而不是之前,而不是之后.


alo*_*ica 5

来自 msdn:

延迟执行

延迟执行意味着表达式的计算被延迟到实际需要它的实现值。当您必须操作大型数据集合时,延迟执行可以极大地提高性能,尤其是在包含一系列链式查询或操作的程序中。在最好的情况下,延迟执行只允许通过源集合进行一次迭代。

当在迭代器块中使用时,yield 关键字(以 yield-return 语句的形式)在 C# 语言中直接支持延迟执行。这样的迭代器必须返回类型IEnumeratorIEnumerator<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();
Run Code Online (Sandbox Code Playgroud)

急切与懒惰的评估

当您编写实现延迟执行的方法时,您还必须决定是使用惰性求值还是急切求值来实现该方法。

  • 在惰性求值中,在每次调用迭代器期间处理源集合的单个元素。这是实现迭代器的典型方式。
  • 在急切求值中,对迭代器的第一次调用将导致处理整个集合。可能还需要源集合的临时副本。

惰性求值通常会产生更好的性能,因为它在整个集合求值过程中平均分配开销处理,并最大限度地减少临时数据的使用。当然,对于某些操作,除了实现中间结果之外别无选择。

来源