众所周知, C# 可以通过 yield
语句来快速向 IEnumerator
或者 IEnumerable
类型的方法返回值返回一个元素. 但它还有另外一个特性, 就是其内部逻辑的懒执行. 每两个 yield
语句之间的逻辑都是一个状态, 只有在调用迭代器的 MoveNext
方法后, 才会执行下一个状态的逻辑.
在文章中, 编译后的代码已经经过简化和删减, 以便于理解
迭代器方法的懒执行
举一个简单的例子:
IEnumerator SomeLogic()
{Console.WriteLine("hello world");yield return null;Console.WriteLine("fuck you world");
}
在调用的时候, 逻辑并不会被立即执行, 只有对其返回的迭代器调用 MoveNext
的时候, 才会继续执行.
var enumerator = SomeLogic();enumerator.MoveNext(); // 打印 hello world
enumerator.MoveNext(); // 打印 fuck you world
迭代器方法的编译
在 C# 中, 使用了 yield 语句的, 返回 IEnumerator 或 IEnumerable 的方法, 会由编译器生成一个迭代器或可迭代类型, 在类型的内部包含该方法的逻辑.
例如上面提到的代码, 它会被编译成大概这样:
IEnumerator SomeLogic()
{return new SomeLogicEnumerator();
}private sealed class SomeLogicEnumerator : IEnumerator, IDisposable
{private int state;private object current;object IEnumerator.Current{get{return current;}}public SomeLogicEnumerator(int state){this.state = state;}void IDisposable.Dispose(){}private bool MoveNext(){int num = state;if (num != 0){if (num != 1){return false;}state = -1;Console.WriteLine("AWA");return false;}state = -1;Console.WriteLine("QWQ");current = null;state = 1;return true;}bool IEnumerator.MoveNext(){//ILSpy generated this explicit interface implementation from .override directive in MoveNextreturn this.MoveNext();}void IEnumerator.Reset(){throw new NotSupportedException();}
}
我们可以看到, 在这个迭代器类型中, 有一个 state
字段存储了当前的状态, 而在 MoveNext
被调用时, 会切换当前状态, 然后根据当前状态执行对应逻辑.
当然, 如果你的迭代器逻辑稍微长一些, 它也是可以处理的.
IEnumerator SomeLogic()
{Console.WriteLine("QWQ");yield return 1;Console.WriteLine("AWA");yield return 2;Console.WriteLine("QJFD");yield return 3;Console.WriteLine("JWOEIJFOIWE");
}
它的 MoveNext
就变成了这样:
private bool MoveNext()
{switch (state){case 0:Console.WriteLine("QWQ");current = 1;state = 1;return true;case 1:Console.WriteLine("AWA");current = 2;state = 2;return true;case 2:Console.WriteLine("QJFD");current = 3;state = 3;return true;case 3:state = -1;Console.WriteLine("JWOEIJFOIWE");return false;default:return false;}
}