Итерация по IEnumerable останавливается после первой итерации

У меня есть следующее в моем Main() метод

Reeks r = new Reeks();

foreach(int i in r){
    if(i < 1000){
        Console.WriteLine(i);
    }
}

Что я хочу, так это перебирать Reeks до значения i > 1000 где i предыдущее значение, умноженное на 2, т.е. [1,2,4,8,16,32],

Класс Reeks выглядит так

class Reeks : IEnumerable<int>, IEnumerator<int>
{
    private List<int> reeks;
    private int idx = -1;

    public Reeks()
    {
        reeks = new List<int>() {1};
    }

    public IEnumerator<int> GetEnumerator()
    {
        return this;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this;
    }

    public void Dispose()
    {

    }

    public bool MoveNext()
    {
        if (idx < reeks.Count - 1)
        {
            idx++;
            return true;
        }
        return false;
    }

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

    public int Current
    {
        get
        {
            if (idx == -1)
            {
                throw new InvalidOperationException("Enumeration has not started. Call MoveNext");
            }
            else
            {
                if (idx != 0)
                {
                    reeks.Add((reeks[(idx-1)]*2));
                }
                return reeks[idx];
            }
        }
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }
}

Проблема в том, что он перестает повторять после первой итерации, просто печатает 1 в начале, а не значения, которые должны прийти после этого.

2 ответа

Решение

Не уверен, зачем тебе нужен List<T> там. if (idx < reeks.Count - 1) в MoveNext терпит неудачу во второй итерации.

Если нет необходимости реализовывать свой пользовательский итератор, я бы сделал это с блоками итератора.

private static IEnumerable<int> Reeks()
{
    int num = 1;
    yield return num;

    while (true)
    {
        num = num * 2;
        yield return num;
    }
}

Тогда используйте это как

foreach (int i in Reeks().TakeWhile(x => x < 1000))
{
    Console.WriteLine(i);
}

Как вы сказали в комментариях, это должен быть пользовательский итератор. Рукописные блоки итераторов выглядят примерно так. Обратите внимание List<T> удален, вам не нужен список. Вам просто нужно знать, какое последнее значение возвращается.

class Reeks : IEnumerable<int>, IEnumerator<int>
{
    private int current;
    private int idx = -1;

    public IEnumerator<int> GetEnumerator()
    {
        return this;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this;
    }

    public void Dispose()
    {
        Reset();
    }

    public bool MoveNext()
    {
        if (idx == -1)
        {
            idx = 0;
            current = 1;
        }
        else
        {
            current = current * 2;
        }

        return true;
    }

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

    public int Current
    {
        get
        {
            if (idx == -1)
                throw new InvalidOperationException("Enumeration has not started. Call MoveNext");

            return current;
        }
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }
}

Ну, это легко. Ваш List имеет только один элемент. При переборе Reeks (и, следовательно, MoveNext()Вы увеличиваете свой idx на 1. Все еще в порядке.

Тем не менее, ваш MoveNext() определяется как

public bool MoveNext()
{
    if (idx < reeks.Count - 1)
    {
        idx++;
        return true;
    }
    return false;
}

Давайте переберем первые два;

Итерация 1

idx = -1;
reeks.Count = 1; //Because reeks is a List containing 1 element

if(-1 < 0)
{
    idx++;
    return true;
}

Который работает.

Итерация 2

idx = 0; //incremented -1 by 1
reeks.Count = 1; //Not altered

if(0 < 0)
{
    //Not reached!
}

return false;

Который вернет ложь.

редактировать

Ответ, предоставленный Шрирамом, вероятно, самый лучший и самый элегантный. Однако если вам нужно придерживаться имеющегося у вас кода, вы действительно можете добавить следующий элемент в свой код. MoveNext() метод. Это также сделает его бесконечным.

public bool MoveNext()
{
    int size = reeks.Count - 1;
    if (idx < size)
    {
        idx++;
        reeks.Add(reeks[size]*2);
        return true;
    }
    return false;
}
Другие вопросы по тегам