C#中的迭代器(非常详细)
迭代器是包含一个或者多个 yield 语句的方法、属性或者索引器。迭代器必须返回以下四个接口之一(否则编译器会产生相应错误):
我们可以一次使用多个 yield 语句,例如:
我们可以将 Foo 修改为如下示例:
但是可以在只带有 finally 语句块的 try 语句块中使用 yield 语句:
当显式使用枚举器时,一个陷阱是提前结束枚举而不销毁枚举器,从而绕过 finally 语句块的执行。我们可以将枚举器的使用显式包裹在 using 语句中来避免上述错误。
// Enumerable interfaces System.Collections.IEnumerable System.Collections.Generic.IEnumerable<T> // Enumerator interfaces System.Collections.IEnumerator System.Collections.Generic.IEnumerator<T>
我们可以一次使用多个 yield 语句,例如:
foreach (string s in Foo())
Console.WriteLine(s); // Prints "one", "Two", "Three"
IEnumerable<string> Foo()
{
yield return "one";
yield return "Two";
yield return "Three";
}
yield break语句
return 语句在迭代器块中是非法的。如果希望提前退出迭代器块,应该使用 yield break 语句。我们可以将 Foo 修改为如下示例:
IEnumerable<string> Foo(bool breakEarly)
{
yield return "one";
yield return "Two";
if (breakEarly)
yield break;
yield return "Three";
}
迭代器和try/catch/finally语句块
yield return 语句不能出现在带有 catch 子句的 try 语句块中:
IEnumerable<string> Foo()
{
try { yield return "one"; } // Illegal
catch { ... }
}
yield return 语句也不能出现在 catch 或者 finally 语句块中。出现这些限制的原因是编译器必须将迭代器转换为带有 MoveNext、Current 和 Dispose 成员的普通类,而转换异常处理语句块会大大增加代码的复杂性。但是可以在只带有 finally 语句块的 try 语句块中使用 yield 语句:
IEnumerable<string> Foo()
{
try { yield return "one"; } // OK
finally { ... }
}
当枚举器到达序列末尾或被销毁时就可以执行 finally 语句块了。如果枚举提前结束,则 foreach 语句会隐式销毁枚举器,这是消费枚举器的安全且正确的做法。当显式使用枚举器时,一个陷阱是提前结束枚举而不销毁枚举器,从而绕过 finally 语句块的执行。我们可以将枚举器的使用显式包裹在 using 语句中来避免上述错误。
string firstElement = null;
var sequence = Foo();
using (var enumerator = sequence.GetEnumerator())
{
if (enumerator.MoveNext())
firstElement = enumerator.Current;
}
ICP备案:
公安联网备案: