眾所周知, 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;}
}