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>();
    }
}

当前回答

这有点离题了,但由于这个问题被标记为最佳实践,我将继续发表我的意见。对于这种类型的东西,我非常喜欢把它变成一个属性:

public static IEnumerable<Product> AllProducts
{
    get {
        using (AdventureWorksEntities db = new AdventureWorksEntities()) {
            var products = from product in db.Product
                           select product;

            return products;
        }
    }
}

当然,它更像一个样板,但使用它的代码将看起来更干净:

prices = Whatever.AllProducts.Select (product => product.price);

vs

prices = Whatever.GetAllProducts().Select (product => product.price);

注意:我不会对任何可能需要一段时间才能完成工作的方法这样做。

其他回答

那么这个呢?

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList();
    }
}

我想这里干净多了。不过,我手头没有VS2008可以检查。 在任何情况下,如果Products实现了IEnumerable(似乎-它在foreach语句中使用),我将直接返回它。

考虑到确切的两个代码片段,我认为版本1更好,因为它可以更有效。假设有很多产品,调用者希望将其转换为dto。

var dtos = GetAllProducts().Select(ConvertToDto).ToList();

在版本2中,首先创建一个Product对象列表,然后创建另一个ProductDto对象列表。版本1没有Product对象的列表,只构建了所需的ProductDto对象的列表。

Even without converting, Version 2 has a problem in my opinion: The list is returned as IEnumerable. The caller of GetAllProducts() does not know how expensive the enumeration of the result is. And if the caller needs to iterate more than once, she will probably materialize once by using ToList() (tools like ReSharper also suggest this). Which results in an unnecessary copy of the list already created in GetAllProducts(). So if Version 2 should be used, the return type should be List and not IEnumerable.

这两段代码实际上在做两件不同的事情。第一个版本将根据需要拉成员。第二个版本将在您开始对其进行任何操作之前将所有结果加载到内存中。

这个问题没有对错之分。哪一种更可取取决于具体情况。例如,如果您必须在一定时间内完成查询,并且需要对结果执行一些半复杂的操作,那么第二个版本可能更可取。但是要注意大的结果集,特别是在以32位模式运行这段代码时。在使用这个方法时,我已经被OutOfMemory异常咬了好几次。

要记住的关键是:区别在于效率。因此,您可能应该使用任何使代码更简单的方法,并且只在分析之后更改它。

yield的用法与关键字return类似,只是它将返回一个生成器。生成器对象只遍历一次。

Yield有两个好处:

您不需要读取这些值两次; 您可以获得许多子节点,但不必将它们全部放在内存中。

还有一种解释也许能帮到你。

在这种情况下,我将使用代码的版本2。由于您拥有可用产品的完整列表,而这正是此方法调用的“消费者”所期望的,因此需要将完整的信息发送回调用方。

如果该方法的调用者一次只需要“一个”信息,而下一个信息的消耗是按需的,那么使用yield return将是有益的,它将确保当一个单位的信息可用时,执行命令将返回给调用者。

可以使用yield return的例子如下:

复杂的,一步一步的计算,调用者每次等待一个步骤的数据 在GUI中分页-用户可能永远不会到达最后一页,只有子集的信息需要在当前页面上公开

为了回答你的问题,我会使用版本2。