【问题标题】:How to realize right IEnumerator interface for multiple foreach?如何为多个foreach实现正确的IEnumerator接口?
【发布时间】:2011-10-05 08:14:03
【问题描述】:

我有这样的代码:

        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

在上面的代码中一切正常,但是当我使用下一个时:

foreach (int i in t)
   if (i == 2)
     foreach (int p in t)
       //print p
   else
       //print i

它打印(在括号中的第二个循环): 0 1 (3 4) 2 而不是 0 1 (0 1 2 3 4) 2 3 4 我在 List 和 Collection 上对其进行了测试,他们做得对。 我怎样才能达到我所需要的?

【问题讨论】:

  • 附带说明:表明当前实现错误的另一种方式:Console.WriteLine(t.Count()); // 5 Console.WriteLine(t.Count()); // 0 即它仅迭代一次目前(另请注意,永远不应使用 Reset() - 它已被正式弃用,并且在大多数情况下将有意抛出异常)

标签: c# .net ienumerable ienumerator


【解决方案1】:

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

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

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

除非您正在为 .NET 1.1 编写代码,否则如果您发现自己手动编写枚举器,那么很有可能您是在以艰难的方式完成它,并且把错误作为奖励.

如果你真的必须努力做到:

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; }
    } 
}

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

【讨论】:

  • @Praetor12 是的,但是您需要第二种类型来表示枚举数。我会模拟一下... 2 秒。
  • 这似乎比我预期的要难一些,但这是我需要的。非常感谢!
  • @Praetor12 出于病态的好奇,为什么你需要用艰难的方式编写枚举器?
  • 我喜欢编写我理解的代码。我不知道 yeild 究竟是如何工作的 :( 仅此而已。
  • @Praetor12 你可能会喜欢Eric Lippert's blogpost series on Iterator Blocks,更具体地说是Raymond Chen 的博文和Jon Skeet article 链接自part 2
【解决方案2】:
foreach (int i in t)
   if (i == 2)
     foreach (int p in t)
       //print p
   else
       //print i

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

foreach (int i in t) {
   if (i == 2) {
     foreach (int p in t) {
       //print p
      }
   } else {
       //print i
   }
 }

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-15
    • 2016-01-09
    • 1970-01-01
    相关资源
    最近更新 更多