考虑以下示例:
class YieldTest
{
static void Main(string[] args)
{
var res = Create(new string[] { "1 12 123", "1234", "12345" });
}
static IEnumerable<int> Create(IEnumerable<string> strings)
{
foreach(string s in strings)
{
yield return s.Length;
if(s.Contains(' '))
{
string[] tokens = s.Split(' ');
foreach(string t in tokens)
{
yield return t.Length;
}
}
}
}
}
对Create
的调用会返回{8,1,2,3,4,5}。
让我感到困惑的是执行yield return语句后的代码。
(他们为什么要将它命名为yield return
而不只是yield
?)
documentation告诉我们
在迭代器方法中达到yield return语句时, 返回表达式,并保留代码中的当前位置。
这是什么意思?返回何处发生?什么是迭代器方法?
答案 0 :(得分:11)
这意味着编译器会将您的代码转换为状态机。
当您拨打Create
方法时,您将获得IEnumerable<T>
。然后,您可以在其上调用GetEnumerator()
并获取IEnumerator<T>
。
现在,每次在此迭代器上调用MoveNext
时,代码都会执行,直到找到第一个yield
语句(无论是yield return
还是yield break
)。
如果它命中yield return x
,MoveNext
返回true,迭代器的Current
属性将设置为x
,其余代码将在下一个执行你拨打MoveNext
的时间。
在没有更多代码要运行或代码命中yield break
之前,会发生这种情况。此时,MoveNext
将返回false。
yield return 1;
yield return 2;
yield return 3;
在这段代码上调用MoveNext
将在前三次返回true,并且在每次迭代时,Current
将设置为1,2和3。
编辑2 :
关于yield return ??
语法,来自Eric Lippert的Ambiguous Optional Parentheses, Part Three
当他们设计C#2.0时,他们遇到了这个问题:
yield(x);
这是否意味着“在迭代器中产生x”或“使用参数x调用yield方法?”通过将其更改为
现在它毫不含糊;它不可能意味着“称之为收益率方法”。
yield return(x);
答案 1 :(得分:2)
您的报价中最重要的部分是:
并保留代码中的当前位置。
迭代器方法在退出方法时不会返回,否则您只能枚举一个项目。 yield return
返回一个枚举的元素,然后继续在yield return
的位置运行迭代器方法的代码,以便在遇到下一个yield return
时返回下一个元素。
迭代器方法迭代集合并使用yield return
提供单个项目。这些语句允许简化IEnumerable接口的实现。在早期版本的.NET中,您必须实现一个特定的类作为枚举器。由于这意味着为IEnumerable的每个实现引入了许多样板代码,因此引入了yield
语句以更简单的方式创建迭代器方法。迭代器方法中的yield return
语句向编译器提供了一个返回新元素的信号。在构建项目时,编译器将迭代器方法传递给IEnumerable的实现。
答案 2 :(得分:1)
这样的迭代器方法将一直运行,直到它从它生成的迭代器获取新值时发现yield return。我从技术上讲是在调用枚举器的MoveNext
方法时。
当你调用MoveNext
时,下一次执行将从它在迭代器方法中停止的地方继续执行并继续执行直到它到达下一个yield return。
基本上,在迭代器方法中每次调用yield return都会产生最终可枚举中的一个值。