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>();
}
}
作为理解何时应该使用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,因为您已经有了完整的列表。
以下是Chris Sells在《c#程序设计语言》中讲述的语句;
I sometimes forget that yield return is not the same as return , in
that the code after a yield return can be executed. For example, the
code after the first return here can never be executed:
int F() {
return 1;
return 2; // Can never be executed
}
In contrast, the code after the first yield return here can be
executed:
IEnumerable<int> F() {
yield return 1;
yield return 2; // Can be executed
}
This often bites me in an if statement:
IEnumerable<int> F() {
if(...) {
yield return 1; // I mean this to be the only thing returned
}
yield return 2; // Oops!
}
In these cases, remembering that yield return is not “final” like
return is helpful.