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类似,只是它将返回一个生成器。生成器对象只遍历一次。

Yield有两个好处:

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

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

其他回答

我知道这是一个老问题,但我想提供一个例子,说明如何创造性地使用yield关键字。我真的从这个技巧中受益良多。希望这将有助于任何人谁偶然遇到这个问题。

注意:不要认为yield关键字仅仅是构建集合的另一种方式。收益率的很大一部分力量来自于这样一个事实,即在执行过程中暂停 方法或属性,直到调用代码遍历下一个值。以下是我的例子:

使用yield关键字(与Rob Eisenburg的Caliburn一起使用)。微协程实现)允许我像这样表达对web服务的异步调用:

public IEnumerable<IResult> HandleButtonClick() {
    yield return Show.Busy();

    var loginCall = new LoginResult(wsClient, Username, Password);
    yield return loginCall;
    this.IsLoggedIn = loginCall.Success;

    yield return Show.NotBusy();
}

这将做的是打开我的BusyIndicator,在我的web服务上调用Login方法,将我的IsLoggedIn标志设置为返回值,然后关闭BusyIndicator。

Here's how this works: IResult has an Execute method and a Completed event. Caliburn.Micro grabs the IEnumerator from the call to HandleButtonClick() and passes it into a Coroutine.BeginExecute method. The BeginExecute method starts iterating through the IResults. When the first IResult is returned, execution is paused inside HandleButtonClick(), and BeginExecute() attaches an event handler to the Completed event and calls Execute(). IResult.Execute() can perform either a synchronous or an asynchronous task and fires the Completed event when it's done.

LoginResult看起来像这样:

public LoginResult : IResult {
    // Constructor to set private members...

    public void Execute(ActionExecutionContext context) {
        wsClient.LoginCompleted += (sender, e) => {
            this.Success = e.Result;
            Completed(this, new ResultCompletionEventArgs());
        };
        wsClient.Login(username, password);
    }

    public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
    public bool Success { get; private set; }
}

它可能有助于设置类似这样的东西,并逐步执行以观察发生了什么。

希望这能帮助到一些人!我真的很喜欢探索不同的收益使用方式。

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

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

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

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

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

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

Yield有两个好处:

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

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

假设您的产品LINQ类使用类似的yield来枚举/迭代,第一个版本更有效,因为它每次迭代只产生一个值。

第二个例子是使用ToList()方法将枚举器/迭代器转换为列表。这意味着它手动遍历枚举器中的所有项,然后返回一个平面列表。

那么这个呢?

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语句中使用),我将直接返回它。