yield关键字是c#中一直困扰我的关键字之一,我从来都不确定自己是否正确地使用了它。
在以下两段代码中,哪一段是首选的,为什么?
版本1:使用收益率
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
foreach (Product product in products)
{
yield return product;
}
}
}
版本2:返回列表
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
return products.ToList<Product>();
}
}
直接返回列表。好处:
这样就更清楚了
该列表是可重用的。(迭代器不是)不是真的,谢谢Jon
You should use the iterator (yield) from when you think you probably won't have to iterate all the way to the end of the list, or when it has no end. For example, the client calling is going to be searching for the first product that satisfies some predicate, you might consider using the iterator, although that's a contrived example, and there are probably better ways to accomplish it. Basically, if you know in advance that the whole list will need to be calculated, just do it up front. If you think that it won't, then consider using the iterator version.
当我计算列表中的下一项(甚至是下一组项)时,我倾向于使用yield-return。
使用版本2,在返回之前必须有完整的列表。
通过使用yield-return,您实际上只需要在返回前获得下一项。
除此之外,这有助于在更大的时间框架内分散复杂计算的计算成本。例如,如果列表连接到GUI,而用户从未访问到最后一页,则永远不会计算列表中的最终项。
yield-return更可取的另一种情况是IEnumerable表示无限集。考虑素数列表,或者无限随机数列表。您永远不能一次返回完整的IEnumerable,因此使用yield-return以增量方式返回列表。
在您的特定示例中,您拥有完整的产品列表,因此我将使用版本2。
作为理解何时应该使用yield的概念性示例,假设方法ConsumeLoop()处理ProduceList()返回/产生的项:
void ConsumeLoop() {
foreach (Consumable item in ProduceList()) // might have to wait here
item.Consume();
}
IEnumerable<Consumable> ProduceList() {
while (KeepProducing())
yield return ProduceExpensiveConsumable(); // expensive
}
如果没有yield,对ProduceList()的调用可能需要很长时间,因为你必须在返回之前完成列表:
//pseudo-assembly
Produce consumable[0] // expensive operation, e.g. disk I/O
Produce consumable[1] // waiting...
Produce consumable[2] // waiting...
Produce consumable[3] // completed the consumable list
Consume consumable[0] // start consuming
Consume consumable[1]
Consume consumable[2]
Consume consumable[3]
使用yield,它会重新排列,有点交错:
//pseudo-assembly
Produce consumable[0]
Consume consumable[0] // immediately yield & Consume
Produce consumable[1] // ConsumeLoop iterates, requesting next item
Consume consumable[1] // consume next
Produce consumable[2]
Consume consumable[2] // consume next
Produce consumable[3]
Consume consumable[3] // consume next
最后,正如之前许多人已经建议的那样,您应该使用版本2,因为您已经有了完整的列表。