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

通过一个教程(Professional ASP.NET MVC – Nerd Dinner),我遇到了这段代码:

public IEnumerable 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的值在我调用GetRuleViolations()的时间和我尝试实际迭代它的时间之间从null变为Really Geeky Dinner ,将会创建RuleViolation("Title required", "Title")或不?

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

  3. 假设Title为null或为空。 如果我调用GetRuleViolations()然后连续两次迭代结果可枚举,那么将调用new RuleViolation("Title required", "Title")多少次?

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

关于你的问题:

  1. 正如我所说,当你迭代到下一个元素时,每个if都被执行了。
  2. 在上面的例子中确实不需要yield break 。 它的作用是终止枚举。
  3. 每次遍历可枚举时,都会再次强制执行代码。 在相关线路上设置断点并自行测试。

1)举一个更简单的例子:

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

每次从IEnumerator调用MoveNext() ,代码都会从yield点返回并移动到下一个可执行代码行。

2) yield break; 将告诉IEnumerator没有更多的枚举。

3)每次列举一次。

使用yield break;

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

精简版:

1:yield是神奇的“Stop and come later later”关键字,因此已经评估了“active”前面的if语句。

2:yield break显式结束枚举(在switch的情况下认为“break”)

3:每一次。 当然,您可以通过将结果转换为List来缓存结果,然后迭代结果。