Итерация по 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;
}