【发布时间】:2010-09-06 05:42:25
【问题描述】:
我知道yield 是做什么的,我也看过一些例子,但我想不出现实生活中的应用,你有没有用它来解决一些具体的问题?
(理想情况下是无法通过其他方式解决的问题)
【问题讨论】:
我知道yield 是做什么的,我也看过一些例子,但我想不出现实生活中的应用,你有没有用它来解决一些具体的问题?
(理想情况下是无法通过其他方式解决的问题)
【问题讨论】:
实际上我在我的网站IdeaPipe 上以非传统方式使用它
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 小时重新排名时,我将不得不每次根据权限重新拉取它们。
【讨论】:
Enumerable 类上的LINQ 运算符被实现为使用yield 语句创建的迭代器。它允许您链接 Select() 和 Where() 之类的操作,而无需实际枚举任何内容,直到您在循环中实际使用枚举器,通常使用 foreach 语句。此外,由于如果您决定在收集过程中停止调用 IEnumerator.MoveNext() 时只计算一个值,您将节省计算所有结果的性能损失。
迭代器也可用于实现其他类型的惰性求值,其中仅在需要时才对表达式求值。您还可以将 yield 用于更花哨的东西,例如协程。
【讨论】:
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);
}
【讨论】:
一个有趣的用途是作为异步编程的机制,尤其是用于执行多个步骤并且在每个步骤中需要相同数据集的任务。这方面的两个例子是 Jeffery Richters AysncEnumerator Part 1 和 Part 2。并发和协调运行时 (CCR) 也利用了这种技术CCR Iterators。
【讨论】:
使用 yield 可以防止向下转换为具体类型。这很方便确保集合的使用者不会操纵它。
【讨论】:
我意识到这是一个老问题(在 Jon Skeet 之前?)但我最近一直在考虑这个问题。不幸的是,这里的当前答案(在我看来)没有提到 yield 语句最明显的优势。
yield 语句的最大好处是它允许您迭代非常大的列表,并且内存使用效率比使用标准列表要高得多。
例如,假设您有一个返回 100 万行的数据库查询。您可以使用 DataReader 检索所有行并将它们存储在 List 中,因此需要 list_size * row_size 字节的内存。
或者您可以使用 yield 语句创建一个迭代器,并且一次只能在内存中存储一行。实际上,这使您能够为大量数据提供“流式传输”功能。
此外,在使用迭代器的代码中,您使用了一个简单的 foreach 循环,并且可以根据需要决定从循环中跳出。如果您确实提前中断,那么当您只需要前 5 行时(例如),您并没有强制检索整个数据集。
关于:
Ideally some problem that cannot be solved some other way
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);
}
}
【讨论】: