Почему не работает следующее? (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();
,