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

Jor*_*rge 1 .net c# ienumerator design-patterns

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

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

    Console.WriteLine("1");

    yield return 2;
}
Run Code Online (Sandbox Code Playgroud)

我只知道如何手动实现它.

Ond*_*cny 8

实际上,该yield语句是一种语法糖,它使编译器实际生成一个实现IEnumerator<T>接口的类,并使用该yield语句将该方法的主体重写为状态机.

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

在您的特定情况下,这种重写到状态机和相应的完整实现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   
    }
}
Run Code Online (Sandbox Code Playgroud)

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

为了使这个实现可用,相应的IEnumerable<T>实现是必要的,这是非常简单的:

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

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后以下两个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);
    }   
}
Run Code Online (Sandbox Code Playgroud)

注意:在您的GetEnumerator()代码中,调用Console.WriteLine("1");枚举器返回之后1(并且调用者处理它),因此看起来有点奇怪.