如何不使用yield语句实现Iterator模式(IEnumerator<T>)并手动重写GetEnumerator方法
IEnumerator<T> Without yield Great question! When you use the yield keyword, C# secretly generates a state machine class behind the scenes to handle the enumeration flow. To replicate the behavior of your original method manually, we need to build that state machine ourselves by implementing IEnumerator<int> (and IDisposable, since it's required by the generic enumerator interface).
First, let's recap what your original yield method does:
- The first call to
MoveNext()returnstrue, andCurrentis set to 1. - After the caller retrieves that value, the next
MoveNext()executesConsole.WriteLine("1"), setsCurrentto 2, and returnstrue. - A third
MoveNext()call returnsfalse, ending the enumeration.
Step 1: Create the State Machine Class
We'll make a class that tracks the current state of the enumeration, handles transitions, and implements all required interface members:
public class NumberEnumerator : IEnumerator<int>, IDisposable { // Tracks which part of the enumeration we're in private int _enumerationState; // Implements the Current property from IEnumerator<int> public int Current { get; private set; } // Explicit implementation of the non-generic IEnumerator.Current object IEnumerator.Current => Current; public bool MoveNext() { switch (_enumerationState) { // Initial state: return the first value case 0: Current = 1; _enumerationState = 1; return true; // After first yield: execute the console line, then return the second value case 1: Console.WriteLine("1"); Current = 2; _enumerationState = 2; return true; // No more values to return case 2: return false; default: return false; } } // Reset the enumerator to its initial state (rarely used in practice) public void Reset() { _enumerationState = 0; Current = default; } // Dispose method (required by IDisposable; empty here since we have no unmanaged resources) public void Dispose() { // Cleanup logic would go here if we had resources to release } }
Step 2: Rewrite the GetEnumerator Method
Now replace your original yield-based method with one that returns an instance of our manual state machine:
public IEnumerator<int> GetEnumerator() { return new NumberEnumerator(); }
How This Matches the yield Version
This manual implementation behaves exactly like the original yield code. The C# compiler would generate nearly identical state machine logic when it encounters yield return—the only difference is we're writing it explicitly instead of letting the compiler handle it.
Note: In most real-world scenarios, using yield is preferable because it's cleaner and less error-prone. Manual state machines are mostly useful for understanding how enumerators work under the hood, or for rare cases where you need full control over the enumeration flow.
内容的提问来源于stack exchange,提问作者Jorge

