我是C#的新手,在我以前尝试学习的语言中没有看到相当于yield
的内容,并且除了可读性之外我不相信它是有用的。这些年我没有它就活了下来,为什么我需要它呢?
正如我所说的那样,您可以使用yield return
逐个吐出T
类型的值,而不是将这些值收集到IEnumerable<T>
中,然后将整个集合吐出来结束。重点是什么?毕竟,我确定在中断函数执行以复制单个值时会涉及一些开销。也许我会进行一些性能测试,看看它在时间上是否更有效。更重要的是,我想知道你是否可以告诉我一个特定的情况,我需要迭代一个函数收集的一组值,并且只能用yield
来做,或者更好的做法它与yield
。
答案 0 :(得分:4)
我们的想法是即时生成值。 您的值集合可能是无限的,或者生成每个值的成本可能很高。当您foreach
通过IEnumerable
时,您实际上是在{{1}上调用方法},可以用你喜欢的任何方式实现。使用IEnumerator
的函数会自动重新实现为yield
,仅在请求时生成值。当您想要动态生成值时,您还必须编写IEnumerator
的实现,就像替换IEnumerator
函数的实现一样。
使用生成器的某些特定情况可能比创建和返回集合更可取:
yield
它是有意义的。当然,您可以编写循环,但通过将逻辑提取到生成器中,您可以轻松地将文件替换为数据库表或不同格式的文件,例如基本上,在每种情况下,您都不必担心将yield return
视为IEnumerable
,即您将其视为价值流,而不是{{1}的有限值。 1}},您可以使用生成器来节省时间或内存。
答案 1 :(得分:4)
作为迭代器用法的一个例子,请考虑一个数字系列迭代器:
IEnumerable<int> fibo() {
int cur = 0, next = 1;
while(true) {
yield return cur;
next += cur;
cur = next - cur;
}
}
现在我们可以选择如何处理该系列,并且只计算所需的元素:
var fibs = fibo();
var sumOfFirst10Fibs = fibs.Take(10).Sum();
另一个有用的模式是扁平化复杂的数据结构,如树 1 :
public class Tree<T> {
public Tree<T> Left, Right;
public T value;
public IEnumerable<T> InOrder() {
if(Left != null) {
foreach(T val in Left.InOrder())
yield return val;
}
yield return value;
if(Right != null) {
foreach(T val in Right.InOrder())
yield return val;
}
}
}
}
1正如Alexey在评论中指出的那样,有序遍历效率低下(特别是在遍历高大的树木时)。
答案 2 :(得分:0)
MSDN涵盖了很多内容:
在语句中使用yield关键字时,表示该 方法,运算符或获取它的访问器是迭代器。 使用yield来定义迭代器不需要显式 额外的类(保存枚举状态的类,请参阅 IEnumerator(Of T)的例子)当你实现IEnumerable时 和自定义集合类型的IEnumerator模式。
技术实施
以下代码返回IEnumerable&lt; string&gt;来自迭代器 方法,然后遍历其元素。
IEnumerable<string> elements = MyIteratorMethod(); foreach (string element in elements) { … }
对MyIteratorMethod的调用不会执行方法的主体。 相反,该调用返回IEnumerable&lt; string&gt;进入元素 变量。
在foreach循环的迭代中,MoveNext方法是 要求元素。此调用执行MyIteratorMethod的主体 直到达到下一个yield return语句。表达方式 yield return语句返回的不仅仅是确定值 由循环体消耗的元素变量,以及 元素的当前属性,它是IEnumerable&lt; string&gt;。
在每一个上 foreach循环的后续迭代,执行 迭代器体从它停止的地方继续,再次停止它 达到收益率报表。当foreach循环完成时 到达iterator方法的结尾或yield break语句。
答案 3 :(得分:0)
yield在您想要返回的Collection尚未就绪的场景中非常有用。即你在迭代时建立列表。通过使用yield-return,您实际上只需要在返回之前拥有下一个项目。 另一种情况,其中yield-return是优选的,如果IEnumerable表示无限集。考虑素数列表,或无限的随机数列表。你永远不能一次返回完整的IEnumerable,所以你使用yield-return来逐步返回列表。