我知道yield的作用,并且我已经看到了一些示例,但是我无法想到现实生活中的应用程序,您是否使用它来解决某些特定问题?
(理想情况下是无法通过其他方式解决的问题)
我意识到这是一个老问题(乔恩·斯凯特之前?),但是我最近一直在考虑这个问题。不幸的是,目前的答案(我认为)并未提及yield语句的最明显优势。
yield语句的最大好处是,它使您可以比使用标准列表更有效地使用内存来遍历非常大的列表。
例如,假设您有一个数据库查询返回100万行。您可以使用DataReader检索所有行并将它们存储在List中,因此需要内存的list_size * row_size个字节。
或者您可以使用yield语句创建一个Iterator,并且一次只能在内存中存储一??行。实际上,这使您能够对大量数据提供"流"功能。
此外,在使用Iterator的代码中,您使用了一个简单的foreach循环,可以根据需要决定从循环中退出。如果您确实要早点休息,则只需要前5行时就不必强制检索整个数据集。
关于:
1
| Ideally some problem that cannot be solved some other way |
yield语句不会为您提供使用您自己的自定义迭代器实现所无法完成的任何工作,但它省去了编写通常所需的复杂代码的麻烦。只有很少的问题(如果有的话)解决不了一种以上的方法。
以下是一些最近的问题和答案,提供了更多详细信息:
增加了关键字价值吗?
在LINQ之外,产量是否有用?
实际上,我在我的网站IdeaPipe
上以非传统方式使用它
1 2 3 4 5 6 7 8 9
| 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迭代器。
使用收益率可以防止降级为具体类型。这很容易确保集合的使用者不会对其进行操作。
yield的另一个很好的用途是对IEnumerable的元素执行功能,并返回不同类型的结果,例如:
1 2 3 4 5 6 7
| public delegate T SomeDelegate(K obj);
public IEnumerable< T > DoActionOnList(IEnumerable<K> list, SomeDelegate action)
{
foreach (var i in list)
yield return action(i);
} |
Enumerable类上的
LINQ \\运算符实现为使用yield语句创建的迭代器。它允许您链接Select()和Where()之类的操作,而无需实际枚举任何内容,直到您在循环中实际使用枚举器(通常通过使用foreach语句)为止。另外,由于如果您决定停止中间收集,则在调用IEnumerator.MoveNext()时仅计算一个值,因此可以节省计算所有结果的性能。
迭代器还可以用于实现其他类型的惰性求值,其中仅在需要时才对表达式求值。您还可以将yield用于更多喜欢的东西,例如协程。
您也可以使用yield return将一系列函数结果视为列表。例如,考虑一家公司每两周向其员工支付一次工资。可以使用以下代码检索工资单日期的子集作为列表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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);
}
} |