C# IEnumerable, Функция Сброса IEnumerator Не Вызывается

Я в основном пытаюсь сделать свой класс способным повторять с помощью foreach. Я читал этот учебник. MSDN. Это кажется очень прямолинейным. Однако у меня есть проблема, когда я хочу повторить второй раз. Я отладил его; и оказалось, что он не вызывает

4 ответов


каждый раз foreach называется, он просит новая IEnumerator. Возврат экземпляра класса-плохая идея - вы должны создать отдельный класс для реализации IEnumerator, и верните его вместо этого.

это часто делается с помощью вложенного (частного) класса и возвращает его экземпляр. Вы можете передать экземпляр класса A в частный класс (предоставив ему доступ к data), и поставить position поле в этом классе. Это позволит более чем одному перечислителю быть создан simulatenously и будет работать должным образом с последующими вызовами 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();
    }
}

наконец, вы можете использовать итераторы чтобы реализовать это гораздо проще:

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<int> в дополнение к IEnumerable. Дженерики делают это намного лучше с точки зрения использования.


перечисление не вызывает 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);
        }
    }
}

Reset () был в основном ошибкой. Уже известен метод получения чистого перечислителя, если это возможно: GetEnumerator ().

Это требование в спецификации, что реализации блока итератора (для итератора) вызывают исключение для этого метода, следовательно, в общем случае это Формально известный что он не может работать, и просто: никто никогда не называет его. Честно говоря, они также должны были отметить его [устаревший] на API !

кроме того, многие последовательности не повторяются. Думаю итераторов сел на объекте networkstream или генератор случайных нумер. Поскольку итераторы (в общем случае) не обязательно должны быть повторяемыми, вы должны стремиться, где это возможно, повторять их не более одного раза. Возможно, буферизация через ToList (), если это невозможно.

"foreach" не включает Reset() в любой момент. Просто GetEnumerator (), MoveNext (), Current и Dispose ().


и Рид, и Карлос правы. Вот один из способов, которым вы можете это сделать, так как int[] осуществляет IEnumerable и IEnumerable<int>

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