首页 > 编程笔记 > C#笔记 阅读:2

C#中的迭代器(非常详细)

迭代器是包含一个或者多个 yield 语句的方法、属性或者索引器。迭代器必须返回以下四个接口之一(否则编译器会产生相应错误):
// 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;
}

相关文章