C#IEnumerable,IEnumerator重置函数未被调用

Sai*_*ait 7 c# foreach enumerable enumerator

我基本上试图使我的类能够迭代使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行以下主要功能时; 它从不调用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

0
1
2
3
4
--- First foreach finished. ---
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)

有什么想法吗?

Ree*_*sey 13

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

    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,您也可以直接返回数组的枚举器(虽然我假设您正在尝试学习如何创建干净的枚举器):

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

    public IEnumerator GetEnumerator()
    {
        return data.GetEnumerator();
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,您可以使用迭代器以更简单的方式实现它:

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

话虽这么说,我强烈建议IEnumerable<int>除了IEnumerable之外还要实现.仿制药在使用方面做得更好.


Mar*_*ell 6

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

这是一个需要在迭代器块实现(迭代器)规范抛出一个异常,此方法,因此它是一般情况下的正式名称,它不能指望工作,简单地说:没有人会调用它。坦率地说,他们也应该在 API 上将其标记为 [Obsolete]!

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

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


car*_*ira 5

枚举不调用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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)