我知道什么 yield 确实如此,而且我看过一些例子,但我想不出现实生活中的应用程序,你用它来解决一些具体问题吗?

(理想情况下是一些无法通过其他方式解决的问题)

有帮助吗?

解决方案

我意识到这是一个老问题(乔恩·斯基特之前?),但我最近一直在考虑这个问题。不幸的是,这里当前的答案(在我看来)没有提到yield语句最明显的优点。

yield 语句的最大好处是它允许您迭代非常大的列表,并且比使用标准列表更有效地使用内存。

例如,假设您有一个返回 100 万行的数据库查询。您可以使用 DataReader 检索所有行并将它们存储在 List 中,因此需要 list_size * row_size 字节的内存。

或者,您可以使用yield 语句创建一个迭代器,并且一次只在内存中存储一​​行。实际上,这使您能够为大量数据提供“流”功能。

此外,在使用迭代器的代码中,您可以使用简单的 foreach 循环,并且可以根据需要决定从循环中跳出。如果您确实提前中断,那么当您只需要前 5 行时(例如),您就不会强制检索整个数据集。

关于:

Ideally some problem that cannot be solved some other way

Yield 语句不会为您提供使用自己的自定义迭代器实现无法完成的任何操作,但它使您无需编写所需的复杂代码。很少有问题(如果有的话)不能通过多种方法解决。

以下是一些最近的问题和答案,提供了更多详细信息:

产生关键词附加值?

在 LINQ 之外,yield 有用吗?

其他提示

实际上我在我的网站上以非传统方式使用它 创意管

public override IEnumerator<T> GetEnumerator()
{
    // goes through the collection and only returns the ones that are visible for the current user
    // this is done at this level instead of the display level so that ideas do not bleed through
    // on services
    foreach (T idea in InternalCollection)
        if (idea.IsViewingAuthorized)
            yield return idea;
}

所以基本上它会检查当前是否授权查看该想法,如果是,则返回该想法。如果不是,则直接跳过。这允许我缓存想法,但仍然向授权的用户显示想法。否则,我每次都必须根据权限重新拉取它们,而它们每 1 小时才重新排名一次。

一个有趣的用途是作为异步编程的机制,特别是用于需要多个步骤并且在每个步骤中需要相同数据集的任务。Jeffery Richters AysncEnumerator 就是这方面的两个例子 第1部分第2部分. 。并发和协调运行时 (CCR) 也使用了这种技术 CCR 迭代器.

Enumerable 类上的 LINQ 运算符被实现为使用yield 语句创建的迭代器。它允许您链接像 Select() 和Where() 这样的操作,而无需实际枚举任何内容,直到您在循环中实际使用枚举器为止,通常通过使用 foreach 陈述。此外,由于如果您决定在收集过程中停止,则在调用 IEnumerator.MoveNext() 时仅计算一个值,因此您将节省计算所有结果的性能损失。

迭代器还可以用于实现其他类型的惰性计算,其中仅在需要时才计算表达式。您还可以使用 屈服 对于像协程这样更奇特的东西。

Yield 的另一个很好的用途是对 IEnumerable 的元素执行函数并返回不同类型的结果,例如:

public delegate T SomeDelegate(K obj);

public IEnumerable<T> DoActionOnList(IEnumerable<K> list, SomeDelegate action)
{
    foreach (var i in list)
        yield return action(i);
}

使用yield可以防止向下转型为具体类型。这可以方便地确保集合的使用者不会操纵它。

您还可以使用 yield return 将一系列函数结果视为列表。例如,考虑一家每两周向员工支付工资的公司。人们可以使用以下代码以列表形式检索工资单日期的子集:

void Main()
{
    var StartDate = DateTime.Parse("01/01/2013");
    var EndDate = DateTime.Parse("06/30/2013");
    foreach (var d in GetPayrollDates(StartDate, EndDate)) {
        Console.WriteLine(d);
    }
}

// Calculate payroll dates in the given range.
// Assumes the first date given is a payroll date.
IEnumerable<DateTime> GetPayrollDates(DateTime startDate, DateTime endDate, int     daysInPeriod = 14) {
    var thisDate = startDate;
    while (thisDate < endDate) {
        yield return thisDate;
        thisDate = thisDate.AddDays(daysInPeriod);
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top