2015-01-23 2 views
2

Проверить эту программу:для каждого цикла в методе итератора

static class Program 
{ 
    static void Main() 
    { 
    GetLinks(); 
    Console.WriteLine("Program failed!"); 
    } 

    static IEnumerable<string> GetLinks() 
    { 
    throw new Exception(); 
    foreach (var item in new string[] { }) 
     yield return item; 
    } 
} 

Это очень странно, но результатом этой программы является Program failed!, то есть функция GetLinks даже не называется.
Какое объяснение этого поведения?

Check it out for yourself.

ответ

3

Итерационные блоки ленивы. Вы должны вызвать его, вызвав его в foreach или что-то в этом роде. Код внутри вашего блока итератора будет выполняться только при первом вызове MoveNext, который foreach сделает для вас.

На данный момент вы только что готовили запрос, его необходимо оформить, позвонив по телефону GetEnumerator, а затем следуйте по адресу MoveNext.

Например, следующий код не будет работать, как ожидалось.

static void Main() 
{ 
    foreach(var item in GetLinks()) 
     Console.WriteLine(item); 
    Console.WriteLine("Program failed!"); 
} 

Дальнейшее чтение Iterator block implementation details

+0

и вот [документация] (https://msdn.microsoft.com/en-us/library/9k7k7cf0 .aspx): _ "... Техническая реализация: вызов MyIteratorMethod не выполняет тело метода. Вместо этого вызов возвращает IEnumerable в переменную элементов ..." _. Так как тело метода даже не выполнено, исключение не возникает. –

1

Это происходит из-за ленивого исполнения. Попробуйте потребляя результат вашей функции, например:

GetLinks().First(); 
+0

Справа, забыл. – Shimmy

3

Итератора блок представляет собой несколько особый метод ... Когда он видит yield return ключевое слово, компилятор генерирует класс, который реализует IEnumerator<T> (а также IEnumerable<T>, в зависимости от возвращаемый тип вашего метода) и изменяет ваш метод, чтобы вернуть экземпляр этого класса. Исходное тело вашего метода преобразуется в метод нумератора MoveNext. Это означает, что тело вашего метода выполняется только после того, как результат будет указан. Просто вызов метода не влияет, если вы не перечисляете результат. Другими словами, блоки итераторов выполняются лениво.

Вот почему методы реализованы в виде блоков итераторов должны подтвердить свои аргументы в отдельный метод:

public IEnumerable<string> Foo(string arg) 
{ 
    if (arg == null) throw new ArgumentNullException(); 
    return FooIterator(arg); 
} 

private IEnumerable<string> FooIterator(string arg) 
{ 
    yield return arg; 
}