Оператор yield в C#: назначение и примеры использования
Сегодня речь пойдет про важный оператор языка C# — yield. Мы также рассмотрим тесно связанные с ним понятия контейнера, генератора и итератора. В материале много примеров кода, в частности подробно рассмотрено применение yield break и yield return.

Содержание статьи:
2. Примеры оператора yield (c# yield)
3. Именованный итератор и его примеры
Введение
Предположим, у вас есть контейнер, не имеет значения какой. Этот контейнер содержит некоторый набор элементов. Вам необходимо сослаться на определенное место в этом наборе элементов. Это нужно, например, чтобы затем от этой позиции можно было перейти вперед или назад, при этом выполнив какую-то инструкцию обработки (изменить или вставить элемент, удалить его и т.д.). Чтобы не приходилось писать алгоритмы специфические для каждого контейнера, и были придуманы универсальные итераторы.
Оператор yield — специальный оператор генерации блока итератора. Оператор yield генерирует специальный класс, который выступает в качестве итератора для нашей коллекции. Этот оператор сообщает нам и компилятору, что данное выражение — итератор. Задача итератора перемещаться между элементами коллекции и возвращать текущее значение в цикле.
Примеры оператора yield (c# yield)
Давайте подкрепим эту теорию практикой. Напишем код для вывода всех четных значений первых 20 чисел:
using System;
namespace HL
{
class Program
{
static void Main()
{
int[] a = new int[20];
a = func(2, 20);
for (int i = 0; i < 20; i++)
{
Console.WriteLine(a[i]);
}
Console.Read();
}
public static int[] func(int start, int number)
{
int[] _number = new int[number];
for (int i = 0; i<number; i++)
{
_number[i] = start + 2 * i;
}
return _number;
}
}
}
Теперь воспользуемся оператором yield, чтобы упростить этот код, ускорить работу алгоритма и сократить объем используемой памяти:
using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace demo2
{
class Program
{
static void Main()
{
foreach (var item in func(2, 10))
{
Console.WriteLine(item);
}
Console.Read();
}
public static IEnumerable func(int start, int number)
{
for (int i = 1; i < number; i++)
{
yield return start + 2 * i;
}
}
}
}
Применение yield return возвращает каждый элемент коллекции по очереди путем применения оператора foreach или запроса LINQ. Каждая итерация цикла foreach вызывает метод итератора. При достижении в методе итератора оператора yield return возвращается выражение стоящее после него, а также сохраняется текущее расположение в коде. При следующем вызове функции итератора выполнение возобновляется с этого места.
В примере ниже оператор yield return расположен в цикле for. Каждая итерация оператора foreach создает вызов функции итератора getRange. При этом происходит переход к следующему выполнению оператора yield return, которое осуществляется во время следующей итерации цикла for. Тип возвращаемого значения метода итератора — System.Collections.IEnumerable. При вызове метода итератора возвращается перечисляемый объект, содержащий числа на заданном участке массива.
using System;
namespace DesignPatterns.Iterator.Conceptual
{
public class YieldExample
{
static void Main()
{
int[] arrayOfNumbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
foreach (int i in getRange(arrayOfNumbers, 3, 6))
{
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;
}
}
// Отображение в консоли: 4 5 6 7
}
}
Оператор yield breakслужит для преждевременного завершения перебора элементов коллекции. Ниже приводим пример программы, которая прерывает итерацию после десяти элементов. В консоли выводится десять первых элементов коллекции символов.
using System;
using System.Collections;
class FstClass
{
char ch = 'A';
// В процессе перебора возвращаются первые 13 символов
public IEnumerator GetEnumerator()
{
for (int i = 0; i < 26; i++)
{
if (i == 13) yield break; // остановить итерацию
yield return (char)(ch + i);
}
}
}
class ItrDemo3
{
static void Main()
{
FstClass mc = new FstClass();
foreach (char ch in mc)
Console.Write(ch + " ");
Console.WriteLine();
}
}
Именованный итератор и его примеры
Именованный итератор выглядит следующим образом:
public IEnumerable имя_итератора(набор_параметров) {
// имя_итератора - определенное имя метода
yield return obj;
// obj — следующий объект, возвращаемый итератором
}
Созданный именованный итератор можно применять, скажем, для управления циклом foreach. Именованные итераторы могут передавать аргументы итератору, который управляет алгоритмом перебора элементов в коллекции. Например, итератору можно вернуть значения пределов выборки элементов, возвращаемой из коллекции итератором.
Итог по применению оператора yield
Сводим все приведенные выше концепции воедино. Напишем максимально оптимальный код для вывода букв массива:
using System;
using System.Collections;
namespace abcd
{
class Bukva
{
char ch = 'А';
int end;
public Bukva(int end)
{
this.end = end;
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i < this.end; i++)
{
if (i == 33) yield break; // Остановка итератора, как только заканчивается массив букв
yield return (char)(ch + i);
}
}
// Создание именованного итератора
public IEnumerable imennovaniyItr(int begin, int end)
{
for (int i = begin; i <= end; i++)
{
yield return (char)(ch + i);
}
}
}
class Program
{
static void Main()
{
Console.Write("Какое количество символов показать? ");
int i = int.Parse(Console.ReadLine());
Bukva lt = new Bukva(i);
Console.WriteLine("\nДемонстрация букв: \n");
foreach (char c in lt)
Console.Write(c + " ");
Console.Write("\nВведите пределы\n\nMin: ");
int j = int.Parse(Console.ReadLine());
Console.Write("Max: ");
int y = int.Parse(Console.ReadLine());
Console.WriteLine("\nБуквы в указанном диапазоне: \n");
foreach (char c in lt.imennovaniyItr(j, y))
Console.Write(c + " ");
Console.ReadLine();
}
}
}
Заключение
По данной теме настоятельно рекомендуем посмотреть хорошее видео — в нем автор подробно рассказывает что такое yield return в C#, как работает yield return и когда следует применять yield return:

Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: