在Nerd晚餐教程中有趣地使用了C#yield关键字

通过教程(专业ASP.NET MVC - 书呆子晚餐),我遇到了这段代码:

public IEnumerable<RuleViolation> GetRuleViolations() {
    if (String.IsNullOrEmpty(Title))
        yield return new RuleViolation("Title required", "Title");
    if (String.IsNullOrEmpty(Description))
        yield return new RuleViolation("Description required","Description");
    if (String.IsNullOrEmpty(HostedBy))
        yield return new RuleViolation("HostedBy required", "HostedBy");
    if (String.IsNullOrEmpty(Address))
        yield return new RuleViolation("Address required", "Address");
    if (String.IsNullOrEmpty(Country))
        yield return new RuleViolation("Country required", "Country");
    if (String.IsNullOrEmpty(ContactPhone))
        yield return new RuleViolation("Phone# required", "ContactPhone");
    if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
        yield return new RuleViolation("Phone# does not match country", "ContactPhone");
    yield break;
}

我已经读过yield ,但我想我的理解还是有点朦胧。 看起来要做的是创建一个对象,该对象允许循环访问集合中的项目,而不必实际进行循环,除非并且直到绝对必要时为止。

不过,这个例子对我来说有点奇怪。 我认为它的做法是延迟创建任何RuleViolation实例,直到程序员实际上使用for each或者像.ElementAt(2)这样的LINQ扩展方法请求集合中的特定项目。

除此之外,我还有一些问题:

  • 何时对if语句的条件部分进行评估? 何时GetRuleViolations()或实际迭代枚举? 换句话说,如果在我调用GetRuleViolations()的时间与尝试实际遍历它的时间之间, Title的值从null更改为Really Geeky Dinner ,那么将创建RuleViolation("Title required", "Title")或不?

  • 为什么yield break; 必要? 这里真的在做什么?

  • 假设Title为空或空白。 如果我调用GetRuleViolations()然后遍历所产生的枚举两次new RuleViolation("Title required", "Title")将调用new RuleViolation("Title required", "Title")多少次?


  • 包含yield命令的函数与普通函数的处理方式不同。 调用该函数后面发生的事情是,匿名类型由特定的IEnumerable类型的函数构造而成,该函数创建该类型的对象并将其返回。 匿名类包含执行函数主体的逻辑,直到每次调用IEnumerable.MoveNext一个yield命令为止。 这有点误导,函数的主体并不像一个普通的函数那样在一个批处理中执行,而是以枚举的形式执行,当枚举器向前移动一步时,每个部分都会执行。

    关于你的问题:

  • 正如我所说,当迭代到下一个元素时,每个if都会被执行。
  • 在上面的例子中, yield break确实没有必要。 它所做的是终止枚举。
  • 每次遍历枚举时,都会再次强制执行代码。 在相关线上放置一个断点并为自己测试。

  • 1)以这个更简单的例子:

    public void Enumerate()
    {
        foreach (var item in EnumerateItems())
        {
            Console.WriteLine(item);
        }
    }
    
    public IEnumerable<string> EnumerateItems()
    {
        yield return "item1";
        yield return "item2";
        yield break;
    }
    

    每次从IEnumerator调用MoveNext() ,代码将从yield点返回并移至下一个可执行的代码行。

    2) yield break; 会告诉IEnumerator ,没有什么更多的枚举。

    3)每次枚举一次。

    使用yield break;

    public IEnumerable<string> EnumerateUntilEmpty()
    {
        foreach (var name in nameList)
        {
            if (String.IsNullOrEmpty(name)) yield break;
            yield return name;
        }     
    }
    

    简洁版本:

    1:收益率是神奇的“停止并再次回来”关键字,因此评估了“活跃”之前的if语句。

    2:yield break明确结束枚举(在switch case中认为是“break”)

    3:每一次。 当然,您可以将结果缓存到列表中,然后迭代。

    链接地址: http://www.djcxy.com/p/53775.html

    上一篇: Interesting use of the C# yield keyword in Nerd Dinner tutorial

    下一篇: Is 'yield' keyword a syntactic sugar ? What is its Implementation