如何在没有 yield 语句的情况下实现迭代器模式 (IEnumerator<T>)

How to implement Iterator pattern (IEnumerator<T>) without the yield statement

如何在不使用 yield 关键字的情况下重写 GetEnumerator 方法? 方法代码:

public IEnumerator<int> GetEnumerator()
{
    yield return 1;

    Console.WriteLine("1");

    yield return 2;
}

我就是想知道怎么手动实现。

其实yield语句是一个syntactic sugar让编译器实际生成一个class实现了IEnumerator<T>接口并重写了方法体在状态机中使用 yield 语句。

每个状态都与最终生成序列中下一个元素的一部分代码相关联。这嵌入在 MoveNext() 方法中。状态机可以表示所有必要的构造(序列、选择、迭代),因此所有 C# 代码(方法中的含义语句)都可以这样重写。那是 yield.

的基础 'magic'

在您的特定情况下,这种重写到状态机和 IEnumerator<T>(及其继承的 IEnumerator(非通用)和 IDisposable 接口的相应完整实现将看起来像这样:

public class CustomEnumerator : IEnumerator<int>
{
    public int Current { get; private set; }

    object IEnumerator.Current => this.Current;

    // internal 'position' in the sequence, i.e. the current state of the state machine
    private int position = 0;

    public bool MoveNext()
    {
        // advance to next state 
        // (works for linear algorithms; an alternative is to select the next state at the end of processing the current state)
        position++;

        // perform the code associated with the current state and produce an element
        switch (position)
        {
            // state 1: line 'yield return 1;'
            case 1:
                Current = 1;
                return true;

            // state 2: lines 'Console.WriteLine("1");' and 'yield return 2;'
            case 2:
                Console.WriteLine("1"); // see also note at the end of this answer
                Current = 2;
                return true;

            // there are no other states in this state machine
            default:
                return false;
        }
    }

    public void Reset()
    {
        position = 0;
    }

    public void Dispose()
    {
        // nothing to do here   
    }
}

每次调用 MoveNext(),即 foreach 语句的每次迭代内部发生的事情,都会导致执行一部分代码,直到生成序列中的下一个元素.

要使此实现可用,需要相应的 IEnumerable<T> 实现,这非常简单:

public class CustomEnumerable : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        return new CustomEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

然后下面的两个 foreach 循环将产生完全相同的结果:

void Main()
{
    // custom implementation of IEnumerator<T>
    foreach (int i in new CustomEnumerable())
    {
        Console.WriteLine(i);
    }   

    // your original implementation—will produce same results
    // note: I assume someObject implements IEnumerable<T> and hence your GetEnumerator() method
    foreach (int i in someObject)
    {
        Console.WriteLine(i);
    }   
}

注意:在您的 GetEnumerator() 代码中,对 Console.WriteLine("1"); 的调用是 枚举器 returns 1(并且调用者处理它)所以看起来有点奇怪。