Почему не работает следующее? (IEnumerable/ IEnumerator)

Я пробовал пример на этой странице MSDN. Я пытался изменить GetEnumerator метод. Я знаю, что-то не так в этом, но это соответствует, а затем не работает. Ошибка в том, что перечислитель еще не запущен и MoveNext должен называться, но это называется!

class Program
{
    static void Main(string[] args)
    { 
        foreach (var day in new DaysOfTheWekk())
        {
            Console.WriteLine(day) ;
        }
        Console.ReadLine();
    }
}

public class DaysOfTheWekk: IEnumerable
{
    private string[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    public IEnumerator GetEnumerator()
    {
        days.GetEnumerator().MoveNext();
        yield return days.GetEnumerator().Current;
    }
}

5 ответов

Решение

Почему вы хотите позвонить moveNext? Просто опустите .Current:

public class DaysOfTheWeek: IEnumerable
{
    private string[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

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

В противном случае используйте while цикл с:

public class DaysOfTheWeek: IEnumerable
{
    private string[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

    public IEnumerator GetEnumerator()
    {
        var enumerator = days.GetEnumerator();
        while(enumerator.MoveNext())
        { 
            yield return enumerator.Current;
        }
    }
}

Пояснение: GetEnumerator() Метод всегда возвращает новый перечислитель, поэтому, если вы вызываете GetEnumerator().Currentтогда MoveNext() функция не была вызвана во вновь возвращенном экземпляре! Вместо этого используйте переменную, как указано в моем втором примере.

Ты звонил MoveNext() на другом перечислителе

ваш код эквивалентен

public IEnumerator GetEnumerator()
{
    var enumerator1 = days.GetEnumerator();
    enumerator1.MoveNext();
    var enumerator2 = days.GetEnumerator();
    yield return enumerator2.Current;
}

каждый раз, когда вы звоните GetEnumerator() создается новый перечислитель (по крайней мере, для реализаций BCL IEnumerable) и, как видно из приведенного выше кода, вы создаете два перечислителя и вызываете MoveNext для одного и Current для другого. Это ключевое концептуальное различие между свойствами и методами. Ожидается, что метод вернет результат операции, а свойство должно возвращать то же значение, если только состояние объекта не изменилось. Кажется, что в вашем коде есть логическая ошибка: вы возвращаете только первый элемент, и если он отсутствует, он потерпит неудачу, поэтому по сути вы реализовали .Single() метод ваш код будет работать, если вы изменили на

public IEnumerator GetEnumerator()
{
    var enumerator = days.GetEnumerator();
    while(enumerator.MoveNext()){
       yield return enumerator.Current;
    }
}

что, конечно, функционально так же, как

public IEnumerator GetEnumerator()
{
    foreach(var day in days){
       yield return day;
    }
}
days.GetEnumerator().MoveNext();
yield return days.GetEnumerator().Current;

Здесь вы создаете два разных перечислителя. Ты звонишь MoveNext на первом, затем вы создаете еще один в строке ниже и доступ Current на что.

Другое решение заключается в том, что вы должны всегда возвращать один и тот же итератор при вызове метода GetEnumerator(). Это пример реализации:

public class DaysOfTheWeek : IEnumerable
{
    private string[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    private IEnumerator iterator;

    public DaysOfTheWeek()
    {
        iterator = days.GetEnumerator();
        iterator.MoveNext();
    }

    public IEnumerator GetEnumerator()
    {
        return iterator;
    }
}

Вызывать MoveNext() в конструкторе не обязательно, необходимо вызывать метод iterator.MoveNext() перед iterator.current.

Дело в том, что вы всегда будете использовать один и тот же итератор при вызове метода GetEnumerator().

Я думаю, что вы предположили, что days.GetEnumerator() всегда возвращал один и тот же перечислитель. Он возвращает новый каждый раз по уважительной причине - если был только один, разные части кода не могли бы перечислять одновременно. Это не будет хорошо сочиняться.

Вызов days.GetEnumerator() один раз или напиши return days.GetEnumerator();,

Другие вопросы по тегам