16. Оператор yield return. Интерфейс IEnumerable.
yield return
Ключевое слово yield
используется в итераторах для поочерёдного возвращения значения вместо отдельного класса, хранящего состояние перечисления.
yield return используется для возврата каждого элемента коллекции по очереди путем применения оператора foreach
или запроса LINQ. Каждая итерация цикла foreach вызывает метод итератора. При достижении в методе итератора оператора yield return возвращается выражение, стоящее после него и сохраняется текущее расположение в коде. При следующем вызове функции итератора выполнение возобновляется с этого места.
Для завершения итерации можно использовать оператор yield break
.
Важно заметить, что использовать yield
можно только в итераторах
Объявление итератора должно соответствовать следующим требованиям:
Возвращаемый тип должен быть
System.Collections.IEnumerable
,System.Collections.Generic.IEnumerable
,System.Collections.IEnumerator
илиSystem.Collections.Generic.IEnumerator
Объявление не должно содержать параметры
ref
иout
.
Тип yield итератора, который возвращает System.Collections.IEnumerable
или System.Collections.IEnumerator
, — object
. Если итератор возвращает System.Collections.Generic.IEnumerable
или System.Collections.Generic.IEnumerator
, необходимо выполнить неявное преобразование из типа выражения в операторе yield return
в параметр универсального типа.
Методы, в которых операторы yield return
и yield break
использовать нельзя:
Анонимные методы. Дополнительные сведения см. в разделе Анонимные методы.
Методы, содержащие небезопасные (
unsafe
) блоки.
Оператор yield return нельзя размещать в блоке try-catch. Оператор yield return
можно размещать в блоке try
оператора try-finally.
Оператор yield break
можно размещать в блоке try
или catch
, но не в блоке finally
.
Если тело оператора foreach
(вне метода итератора) вызывает исключение, выполняется блок finally
в методе итератора.
Пример
В следующем примере имеется оператор yield return
, расположенный в цикле for
. Каждая итерация тела оператора foreach
создает вызов функции итератора getRange
. При каждом вызове функции итератора происходит переход к следующему выполнению оператора yield return
, которое осуществляется во время следующей итерации цикла for
.
Тип возвращаемого значения метода итератора — System.Collections.IEnumerable
. При вызове метода итератора возвращается перечисляемый объект, содержащий числа на заданном участке массива.
public class YieldExample
{
static void Main()
{
int[] arrayOfNumbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
foreach (int i in getRange(arrayOfNumbers, 2, 4))
{
Console.Write("{0} ", i);
}
}
public static System.Collections.Generic.IEnumerable<int> getRange(int[] numbers, int start, int end)
{
for (int i = start; i <= end; i++)
{
int result = numbers[i];
yield return result;
}
}
// Вывод: 3 4 5
}
Интерфейс IEnumerable
IEnumerable
является базовым интерфейсом для всех неуниверсальных перечислимых коллекций. Универсальная версия этого интерфейса - System.Collections.Generic.IEnumerable<T>
. IEnumerable
содержит только один метод GetEnumerator
, который возвращает IEnumerator
. IEnumerator
позволяет выполнять итерации по коллекции, предоставляя свойство Current
и методы MoveNext
и Reset
.
Рекомендуется реализовать IEnumerable и IEnumerator в пользовательских классах коллекций, чтобы использовать синтаксис foreach
, однако реализация IEnumerable
не является обязательной. Если в вашей коллекции не реализован IEnumerable
, по-прежнему необходимо реализовать итератор для поддержки синтаксиса foreach
, предоставляя метод GetEnumerator
, возвращающий интерфейс, класс или структуру. Необходимо указать класс, содержащий свойство Current
, и методы MoveNext
и Reset
, описанные в IEnumerator
, но при этом класс может не реализовывать интерфейс IEnumerator
.