使用迭代器编写自定义IEnumerator <T>

Ale*_*nov 12 c# iterator

如何编写IEnumerator<T>需要维护某些状态的自定义实现,并仍然可以使用迭代器块来简化它?我能想到的最好的是这样的:

public class MyEnumerator<T> : IEnumerator<T> {
    private IEnumerator<T> _enumerator;
    public int Position {get; private set;} // or some other custom properties

    public MyEnumerator() {
        Position = 0;
        _enumerator = MakeEnumerator();
    }

    private IEnumerator<T> MakeEnumerator() {
        // yield return something depending on Position
    } 

    public bool MoveNext() {
        bool res = _enumerator.MoveNext();
        if (res) Position++;
        return res;
    }

    // delegate Reset and Current to _enumerator as well
}

public class MyCollection<T> : IEnumerable<T> {

    IEnumerator<T> IEnumerable<T>.GetEnumerator() {
        return GetEnumerator();
    }

    public MyEnumerator<T> GetEnumerator() {
        return new MyEnumerator<T>();
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 32

你为什么要写一个迭代器类?迭代器块的重点是你不必......

public IEnumerator<T> GetEnumerator() {
    int position = 0; // state
    while(whatever) {
        position++;
        yield return ...something...;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你添加更多的上下文(即,为什么以上不能工作),我们可能会提供更多帮助.

但是如果可能的话,避免编写迭代器类.他们工作很多,容易出错.

顺便说一下,你真的不必费心Reset- 它在很大程度上已被弃用,并且不应该被使用(因为它不能依赖于任意枚举器).

如果你想使用内部迭代器,那也没关系:

int position = 0;
foreach(var item in source) {
   position++;
   yield return position;
}
Run Code Online (Sandbox Code Playgroud)

或者如果您只有一个枚举器:

while(iter.MoveNext()) {
   position++;
   yield return iter.Current;
}
Run Code Online (Sandbox Code Playgroud)

您也可以考虑将状态(作为元组)添加到您收益的事物中:

class MyState<T> {
    public int Position {get;private set;}
    public T Current {get;private set;}
    public MyState(int position, T current) {...} // assign
}
...
yield return new MyState<Foo>(position, item);
Run Code Online (Sandbox Code Playgroud)

最后,您可以使用LINQ样式的扩展/委托方法,Action<int,T>并为调用者提供位置和值:

    static void Main() {
        var values = new[] { "a", "b", "c" };
        values.ForEach((pos, s) => Console.WriteLine("{0}: {1}", pos, s));            
    }
    static void ForEach<T>(
            this IEnumerable<T> source,
            Action<int, T> action) {
        if (source == null) throw new ArgumentNullException("source");
        if (action == null) throw new ArgumentNullException("action");

        int position = 0;
        foreach (T item in source) {
            action(position++, item);
        }
    }
Run Code Online (Sandbox Code Playgroud)

输出:

0: a
1: b
2: c
Run Code Online (Sandbox Code Playgroud)