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 return对于需要遍历数百万个对象的算法来说非常强大。考虑以下示例,您需要计算可能的拼车行程。首先我们生成可能的行程:
static IEnumerable<Trip> CreatePossibleTrips()
{
for (int i = 0; i < 1000000; i++)
{
yield return new Trip
{
Id = i.ToString(),
Driver = new Driver { Id = i.ToString() }
};
}
}
然后迭代每一次旅行:
static void Main(string[] args)
{
foreach (var trip in CreatePossibleTrips())
{
// possible trip is actually calculated only at this point, because of yield
if (IsTripGood(trip))
{
// match good trip
}
}
}
如果您使用List而不是yield,您将需要为内存分配100万个对象(~190mb),而这个简单的示例将花费~1400ms运行。但是,如果使用yield,就不需要将所有这些临时对象都放到内存中,而且算法速度会大大加快:本例只需要大约400ms就可以运行,完全不消耗内存。
以下是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.
当我计算列表中的下一项(甚至是下一组项)时,我倾向于使用yield-return。
使用版本2,在返回之前必须有完整的列表。
通过使用yield-return,您实际上只需要在返回前获得下一项。
除此之外,这有助于在更大的时间框架内分散复杂计算的计算成本。例如,如果列表连接到GUI,而用户从未访问到最后一页,则永远不会计算列表中的最终项。
yield-return更可取的另一种情况是IEnumerable表示无限集。考虑素数列表,或者无限随机数列表。您永远不能一次返回完整的IEnumerable,因此使用yield-return以增量方式返回列表。
在您的特定示例中,您拥有完整的产品列表,因此我将使用版本2。