Рубріки: Теория

Оператор yield в C#: назначение и примеры использования

Сергій Бондаренко

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

Содержание статьи:

1. Введение

2. Примеры оператора yield (c# yield)

3. Именованный итератор и его примеры

4. Применение итератора

5. Заключение

Введение

Предположим, у вас есть контейнер, не имеет значения какой. Этот контейнер содержит некоторый набор элементов. Вам необходимо сослаться на определенное место в этом наборе элементов. Это нужно, например, чтобы затем от этой позиции можно было перейти вперед или назад, при этом выполнив какую-то инструкцию обработки (изменить или вставить элемент, удалить его и т.д.). Чтобы не приходилось писать алгоритмы специфические для каждого контейнера, и были придуманы универсальные итераторы.

Оператор 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:

 

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023