【问题标题】:C# IEnumerable, IEnumerator Reset Function Not Get CalledC# IEnumerable,IEnumerator 重置函数未被调用
【发布时间】:2012-07-13 14:09:41
【问题描述】:

我基本上是想让我的班级能够使用foreach 进行迭代。我读了这个教程。 MSDN。这似乎很简单。但是,当我想第二次迭代时遇到问题。我调试了它;结果它没有调用Reset()函数。

A 类

class A : IEnumerable, IEnumerator
{
    int[] data = { 0, 1, 2, 3, 4 };

    int position = -1;

    public object Current
    {
        get
        {
            return data[position];
        }
    }

    public bool MoveNext()
    {
        position++;
        return (position < data.Length);
    }

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

    public IEnumerator GetEnumerator()
    {
        return (IEnumerator)this;
    }
}

当我运行以下主要功能时;它从不调用Reset() 函数。所以,在一个循环之后,我再也无法迭代我的类了。

主要

static void Main(string[] args)
{
    A a = new A();

    foreach (var item in a)
    {
        Console.WriteLine(item);
    }

    Console.WriteLine("--- First foreach finished. ---");

    foreach (var item in a)
    {
        Console.WriteLine(item);
    }
}

输出:

0
1
2
3
4
--- First foreach finished. ---
Press any key to continue . . .

有什么想法吗?

【问题讨论】:

  • 看看这个answer
  • 顺便说一句,除非你是为了好玩或一个非常具体的原因,否则你永远不必以这种方式实现迭代器。查看“迭代器块”又名“收益回报”。
  • 我知道它很旧,但我刚刚在谷歌上找到了这篇文章。我遇到了同样的问题,我实现了 MoveNext 方法,当它返回 false 时调用 Reset。

标签: c# foreach enumerable enumerator


【解决方案1】:

里德和卡洛斯都是正确的。这是您可以做到这一点的一种方法,因为int[] 实现了IEnumerableIEnumerable&lt;int&gt;

class A : IEnumerable
{ 
    int[] data = { 0, 1, 2, 3, 4 }; 

    public IEnumerator GetEnumerator() 
    { 
        return data.GetEnumerator(); 
    } 
} 

或者,为了更加强类型,你可以使用IEnumerable的泛型形式:

class A : IEnumerable<int>
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator<int> GetEnumerator()
    {
        return ((IEnumerable<int>)data).GetEnumerator();
    }

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

【讨论】:

  • 我的真实情况比较复杂。这只是一个解释我的问题的例子。在真正的问题中,我没有int 数组。
  • @Zagy 酷。那么,里德和卡洛斯都有很好的答案。 :)
【解决方案2】:

每次调用foreach,它都会要求一个新的 IEnumerator。返回你的类实例是个坏主意——你应该创建一个单独的类来实现IEnumerator,然后返回它。

这通常通过使用嵌套(私有)类并返回它的实例来完成。您可以将 A 类实例传递给私有类(授予它对 data 的访问权限),并将 position 字段放在该类中。它将允许同时创建多个枚举器,并且可以在后续的 foreach 调用中正常工作。

例如,要修改您的代码,您可以执行以下操作:

using System;
using System.Collections;

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator GetEnumerator()
    {
        return new AEnumerator(this);
    }

    private class AEnumerator : IEnumerator
    {
        public AEnumerator(A inst)
        {
            this.instance = inst;
        }

        private A instance;
        private int position = -1;

        public object Current
        {
            get
            {
                return instance.data[position];
            }
        }

        public bool MoveNext()
        {
            position++;
            return (position < instance.data.Length);
        }

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

    }
}

请注意,您也可以直接返回数组的枚举数(尽管我假设您正在尝试学习如何制作干净的枚举数):

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator GetEnumerator()
    {
        return data.GetEnumerator();
    }
}

最后,您可以使用iterators 以更简单的方式实现这一点:

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator GetEnumerator()
    {
        for (int i=0;i<data.Length;++i)
           yield return data[i];
    }
}

话虽如此,我强烈建议在 IEnumerable 之外实现 IEnumerable&lt;int&gt;。泛型在使用方面做得更好。

【讨论】:

  • 是的,你是对的。我试图学习如何制作干净的枚举器。我的真实代码更复杂。
  • @zagy 看看迭代器——它们通常是实现枚举器最好、最实用的方法。话虽这么说,除了IEnumerable 之外,我也总是实现IEnumerable&lt;T&gt; ...
【解决方案3】:

Reset() 基本上是一个错误。如果可能的话,已经有一个已知的方法来获取一个干净的枚举器:GetEnumerator()。

在规范中要求迭代器块实现(对于迭代器)为此方法抛出异常,因此在一般情况下,正式知道不能指望它起作用,而且很简单:没有人调用它。坦率地说,他们也应该在 API 上将其标记为 [已过时]!

此外,许多序列是不可重复的。考虑一下位于 NetworkStream 或随机数生成器上的迭代器。因为迭代器(在一般情况下)不需要是可重复的,所以您应该尽可能地针对它们最多迭代一次。如果不可能的话,也许可以通过 ToList() 进行缓冲。

“foreach”在任何时候都不涉及 Reset()。只需 GetEnumerator()、MoveNext()、Current 和 Dispose()。

【讨论】:

    【解决方案4】:

    枚举不调用Reset。您需要创建一个枚举器的新实例,这可能意味着为枚举器创建一个单独的类(即,IEnumerable 和 IEnumerator 不使用相同的类型),如下面的代码所示:

    public class StackOverflow_11475328
    {
        class A : IEnumerable
        {
            int[] data = { 0, 1, 2, 3, 4 };
    
            public IEnumerator GetEnumerator()
            {
                return new AEnumerator(this);
            }
    
            class AEnumerator : IEnumerator
            {
                private A parent;
                private int position = -1;
    
                public AEnumerator(A parent)
                {
                    this.parent = parent;
                }
    
                public object Current
                {
                    get { return parent.data[position]; }
                }
    
                public bool MoveNext()
                {
                    position++;
                    return (position < parent.data.Length);
                }
    
                public void Reset()
                {
                    position = -1;
                }
            }
        }
        public static void Test()
        {
            A a = new A();
    
            foreach (var item in a)
            {
                Console.WriteLine(item);
            }
    
            Console.WriteLine("--- First foreach finished. ---");
    
            foreach (var item in a)
            {
                Console.WriteLine(item);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-24
      • 2014-09-05
      • 1970-01-01
      • 2021-03-21
      • 1970-01-01
      • 2012-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多