IEnumerable IEnumerator с текущим перемещением и без него

Я пытаюсь понять интерфейс IEnumerable, как он работает, но мне трудно понять концепцию, потому что в некоторых примерах используются IEnumerator, GetEnumerator, Current, moveNext, Reset, а в некоторых примерах демонстрируется IEnumerable с IEnumerator без Current, moveNext, Reset с вложенным внутренним классом.

Мне нужен один простой пример, чтобы понять эту концепцию, и когда уместно использовать IEnumerator с Current, moveNext, Reset, а когда нет.


person 3355307    schedule 15.07.2014    source источник
comment
Потребление? или внедрение?   -  person Marc Gravell    schedule 15.07.2014
comment
@MarcGravell. Буду признателен, если вы предоставите объяснения с обеих точек зрения. Спасибо   -  person 3355307    schedule 15.07.2014
comment
@MarcGravell. С .NET 4.5 мне обычно не нужно внедрять IEnumerator, это правильно?   -  person 3355307    schedule 15.07.2014
comment
на самом деле это не функция среды выполнения .NET; это функция компилятора; IIRC, блоки итераторов существуют с C # 2.0, а .NET 4.5 поставляется с гораздо более высокой версией компилятора, чем эта, так что: да   -  person Marc Gravell    schedule 15.07.2014
comment
@MarcGravell. когда нам действительно нужно использовать IEnumerable?   -  person 3355307    schedule 15.07.2014
comment
foreach циклы и определенные методы LINQ - это наиболее распространенный способ использования IEnumerable. Крайне редко вам нужно делать это вручную.   -  person Ben Aaronson    schedule 15.07.2014


Ответы (2)


Использование Reset() практически никогда не подходит, поскольку этот метод явно ненадежен: например, блоки итератора не поддерживают его. Так что нет смысла беспокоиться об этом.

Если вы имеете в виду с точки зрения его реализации: в большинстве случаев наиболее подходящим вариантом будет использовать блок итератора (он же yield return), потому что делать это вручную сложно, подвержено ошибкам, и обычно не служит большой цели. Вам нужно иметь в виду что-то очень конкретное, чтобы гарантировать реализацию перечислителя вручную.

Некоторые примеры, которые вы видите, могут предшествовать дате добавления блоков итератора в язык; В этом случае у них не было выбора кроме, чтобы написать итератор вручную.

В большинстве случаев: просто позвольте компилятору позаботиться о тяжелой работе. yield return - ваш друг. Примечание: такие вещи, как List<T>, используют настраиваемый итератор с типизированным значением и т. Д., Чтобы минимизировать влияние; обратите внимание, что когда вы foreach над List<T>, вы даже не используете IEnumerable<T> - вы просто используете необработанный GetEnumerator() и связанные с ним API. foreach может использовать IEnumerable<T>, но не требует этого.

person Marc Gravell    schedule 15.07.2014

ПРОВАЙДЕР ========

Вот пример, в котором я отделил класс от перечислителя. MyEnumerableClass является Enumerable, поэтому, если кому-то нужно его перечислить, он может получить перечислитель в классе.

using System;
using System.Collections;


class MyEnumerableClass : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        return new MyEnumerableClassEnumerator();
    }
}

Перечислитель отвечает за перечисление элементов в классе. В этом примере я этого не делал, надеюсь, это более понятно. В этом примере класс MyEnumerableClass и перечислитель в этом классе (MyEnumerableClassEnumerator) не связаны. Обычно перечислитель перечисляет что-то, что содержится в классе, поэтому перечислитель сохраняет ссылку на класс. Кроме того, если состояние класса изменится «значительно», перечислитель не сможет его перечислить, поэтому он должен вызвать InvalidOperationException. Такое поведение обычно достигается добавлением идентификатора версии (например, int versionId) в класс. Когда перечислитель создается, он сохраняет versionId. Если при вызове MoveNext перечислитель видит, что versionId изменился, он вызывает исключение. Таким образом, у вас может быть несколько активных счетчиков в одном классе одновременно. Во всяком случае, вот очень простой пример счетчика

class MyEnumerableClassEnumerator : IEnumerator
{
    int myStatusVariable = 0;

    public object Current
    {
        get { return myStatusVariable; }
    }

    public bool MoveNext()
    {
        return ++myStatusVariable < 10;
    }

    public void Reset()
    {
        myStatusVariable = 0;
    }
}

ПОТРЕБИТЕЛИ ======== Теперь о потребителях. Вы можете использовать счетчик двумя способами: через язык (который генерирует вызовы интерфейса) или самостоятельно генерировать вызовы.

Вот примеры, основанные на классе выше

        MyEnumerableClass myEnumerableClass = new MyEnumerableClass();

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


        IEnumerator enumerator = myEnumerableClass.GetEnumerator();
        while (enumerator.MoveNext())
        {
            Console.WriteLine(enumerator.Current);
        }
person bubi    schedule 15.07.2014