代码之家  ›  专栏  ›  技术社区  ›  devuxer

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

  •  13
  • devuxer  · 技术社区  · 16 年前

    通过一个教程(专业的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 或者像这样的LINQ扩展方法 .ElementAt(2) .

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

    1. 什么时候的条件部分 if 对语句进行评估?什么时候? GetRuleViolations() 是调用的还是实际迭代可枚举的?换句话说,如果 Title 从变化 null Really Geeky Dinner 在我打电话的时候 获取规则冲突() 当我试图在它上面迭代时,会 RuleViolation("Title required", "Title") 是否创建?

    2. 为什么是 yield break; 必要吗?它到底在这里做什么?

    3. 让我们说 书名 为空。如果我打电话 获取规则冲突() 然后在一行中对结果可枚举两次进行迭代,次数为 new RuleViolation("Title required", "Title") 被召唤?

    3 回复  |  直到 16 年前
        1
  •  17
  •   Aviad P.    16 年前

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

    关于你的问题:

    1. 如我所说,每个 if 在迭代到下一个元素时执行。
    2. yield break 在上面的例子中确实没有必要。它的作用是终止枚举。
    3. 每次迭代可枚举项时,都会强制再次执行代码。在相关行上放置一个断点,并为自己测试。
        2
  •  6
  •   ChaosPandion    16 年前

    1)举个简单的例子:

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

    每次你打电话 MoveNext() 来自 IEnumerator 代码从返回 yield 指向并移动到下一个可执行代码行。

    2) yield break; 会告诉 迭代器 没有什么可列举的了。

    3)每次枚举一次。

    使用 产量突破;

    public IEnumerable<string> EnumerateUntilEmpty()
    {
        foreach (var name in nameList)
        {
            if (String.IsNullOrEmpty(name)) yield break;
            yield return name;
        }     
    }
    
        3
  •  2
  •   Benjamin Podszun    16 年前

    短版:

    1:yield是magic“stop and come later”关键字,所以“active”前面的if语句已经过评估。

    2:yield break显式结束枚举(在switch案例中考虑“break”)。

    3:每一次。当然,您可以将结果缓存,例如将其转换为一个列表,然后对其进行迭代。

    推荐文章