如何为多个foreach实现正确的IEnumerator接口?

Vas*_*sya 4 .net c# ienumerable ienumerator

我有一个代码:

        class T : IEnumerable, IEnumerator
        {
            private int position = -1;

            public T() { }

            public IEnumerator GetEnumerator() { return this; }

            public object Current { get { return position; } }

            public bool MoveNext()
            {
                position++;
                return (position < 5);
            }

            public void Reset() { position = -1; }
        }

//Using in code:
T t = new T();
foreach (int i in t)
 //to do something
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,所有工作正常,但是当我使用next时:

foreach (int i in t)
   if (i == 2)
     foreach (int p in t)
       //print p
   else
       //print i
Run Code Online (Sandbox Code Playgroud)

它打印(在括号中的第二个循环):0 1(3 4)2而不是0 1(0 1 2 3 4)2 3 4我在列表和集合上测试它并且它们做得对.我怎样才能达到我的需要?

Mar*_*ell 9

你不能,因为你已经使你的代码表面成为一个枚举器,本身就是IMO的错误.对我来说,更好的版本是:

class T : IEnumerable<int> {
    public IEnumerator<int> GetEnumerator() {
        int i = 0;
        while(i < 5) {
            yield return i;
            i++;
        }
    }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
Run Code Online (Sandbox Code Playgroud)

编译器将使用单独的枚举器创建正确的设备来实现此目的.

除非您是为.NET 1.1编写的,否则如果您发现自己手动编写了一个enumarator,那么很有可能您正在以艰难的方式执行此操作,并将其作为奖励.

如果你真的必须这么做:

class T : IEnumerable<int>
{
    public T() { }

    public IEnumerator<int> GetEnumerator() { return new TEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    private class TEnumerator : IEnumerator<int>
    {
        private int position = -1;
        public int Current { get { return position; } }
        object IEnumerator.Current { get { return Current; } }
        void IDisposable.Dispose() {}
        public bool MoveNext()
        {
            position++;
            return (position < 5);
        }
        public void Reset() { position = -1; }
    } 
}
Run Code Online (Sandbox Code Playgroud)

这里的意义在于TEnumerator允许相同 T实例分别迭代的不同实例.

  • @ Praetor12是的,但你需要第二种类型来代表枚举器.我会嘲笑那...... 2秒. (2认同)
  • @ Praetor12你可能会喜欢[Eric Lippert关于Iterator Blocks的博客系列](http://blogs.msdn.com/b/ericlippert/archive/tags/iterators/),更具体地说是Raymond Chen的博文和[Jon Skeet文章]( http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx)链接自[第2部分](http://blogs.msdn.com/b/ericlippert/archive/2009/07/13/iterator-blocks-part - 两个 - 为什么-NO-REF或出局,parameters.aspx) (2认同)

Ric*_*ard 5

foreach (int i in t)
   if (i == 2)
     foreach (int p in t)
       //print p
   else
       //print i
Run Code Online (Sandbox Code Playgroud)

首先总是使用大括号,而你缩进匹配将发生的另一个if将会混淆事情.

foreach (int i in t) {
   if (i == 2) {
     foreach (int p in t) {
       //print p
      }
   } else {
       //print i
   }
 }
Run Code Online (Sandbox Code Playgroud)

但是你有问题:每个实例只有一个计数器T,并且你使用的是同一个实例.因此,你做了一次.如果要允许并发枚举,则枚举器对象需要与GetEnumerator每次返回新实例分开.