Сегодня речь пойдет про важный оператор языка C# — yield
. Мы также рассмотрим тесно связанные с ним понятия контейнера, генератора и итератора. В материале много примеров кода, в частности подробно рассмотрено применение yield break
и yield return
.
Содержание статьи:
2. Примеры оператора yield (c# yield)
3. Именованный итератор и его примеры
Предположим, у вас есть контейнер, не имеет значения какой. Этот контейнер содержит некоторый набор элементов. Вам необходимо сослаться на определенное место в этом наборе элементов. Это нужно, например, чтобы затем от этой позиции можно было перейти вперед или назад, при этом выполнив какую-то инструкцию обработки (изменить или вставить элемент, удалить его и т.д.). Чтобы не приходилось писать алгоритмы специфические для каждого контейнера, и были придуманы универсальные итераторы.
Оператор yield
— специальный оператор генерации блока итератора. Оператор 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
. Именованные итераторы могут передавать аргументы итератору, который управляет алгоритмом перебора элементов в коллекции. Например, итератору можно вернуть значения пределов выборки элементов, возвращаемой из коллекции итератором.
Сводим все приведенные выше концепции воедино. Напишем максимально оптимальный код для вывода букв массива:
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
:
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…
Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…
В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…
Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…
Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…