在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)
请求集合中的特定项。
不过,除此之外,我还有一些问题:
-
何时评估
if
语句的条件部分? 当GetRuleViolations()
或实际迭代枚举时? 换句话说,如果Title
的值在我调用GetRuleViolations()
的时间和我尝试实际迭代它的时间之间从null
变为Really Geeky Dinner
,将会创建RuleViolation("Title required", "Title")
或不? -
为什么
yield break;
必要? 它到底在做什么? -
假设
Title
为null或为空。 如果我调用GetRuleViolations()
然后连续两次迭代结果可枚举,那么将调用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 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来缓存结果,然后迭代结果。