logo
small logo
  • Продукты
  • Купить
  • Поддержка
  • Статьи
  • Клиентская панель Поддержка
    • en
    • ru
    • pt
    • es
    • de
    • pl
    • JP
    • ZH
  • Главная
  • /
  • Статьи
  • /
  • Foreach в C sharp
  • Семейство генераторов отчетов FastReport - быстрый обзор

    23 марта 2020 г.

    Отчетность – без нее невозможно вести деятельность ни в одной сфере жизнедеятельности. Бюрократия является неодолимой

    Подробнее
  • Как сделать Swiss QR счет в .NET-приложении

    19 мая 2020 г.

    Сейчас многие страны проводят «диджитализацию» платежных систем, то есть оцифровывание. Самый распространенный способ автоматизации платежей

    Подробнее
  • Копирование бэндов отчета в другой со всем содержимым.

    31 марта 2021 г.

    Иногда возникает необходимость использовать части одного отчета в другом. В простых случаях для этого можно

    Подробнее
  • Отчёты и PDF документы в Blazor приложении

    5 апреля 2021 г.

    Недавно Microsoft выпустила веб-платформу под названием Blazor. Этот фреймворк позволяет создать интерактивный веб-интерфейс при помощи

    Подробнее
  • FastCube - высокоскоростной движок OLAP-кубов и Pivot Grid

    29 февраля 2020 г.

    Сложно представить анализ данных без технологии OLAP (On-Line Analytical Processing). Хотя существуют разные мнения по

    Подробнее

Foreach в C sharp

6 октября 2018 г.

Сегодння хотелось бы поговорить о том, как же работает цикл foreach внутри.

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

Цикл foreach рфботает по разному, в зависимости от коллекции, которую нужно перебирать.

1) Если он имеет дело с банальным массивом, то мы всегда можем узнать его длину. Поэтому foreach в конечном итоге будет преобразован в цикл for. Вот, например:

1
2
3
4
5
int[] array = new int[]{1, 2, 3, 4, 5, 6};
foreach (int item in array)
{
 Console.WriteLine(item);
}

Компилятор преобразует цикл в такую конструкцию:

1
2
3
4
5
6
7
8
int[] temp;
int[] array = new int[]{1, 2, 3, 4, 5, 6};
temp = array;
for (int i = 0; i <  temp.Length; i++)
{
 int item = temp[i];
 Console.WriteLine(item);
}

2) Однако, многие коллекции не поддерживают индексированный доступ к элементам, например: Dictionary, Queue, Stack. В этом случае будет использован шаблон итератор.

Этот шаблон основан на интерфейсах System.Collections.Generic.IEnumerator и nongeneric System.Collections.IEnumerator, которые позволяют осуществлять итерацию элементов в наборе.

IEnumerator содержит:

  • Метод MoveNext() - перемещает перечислитель на следующий элемент коллекции;
  • Метод Reset() - перезапускает перечисление, устанавливает перечислитель в начальную позицию;
  • Свойство Current - возвращает текущий элемент коллекции.

IEnumirator наследуется от двух интерфейсов - IEnumirator и IDisposable. Он содержит перегрузку свойства Current предоставляя его реализацию по типу.

Раз уж мы упомянули интерфейс IDisposable, то уделим пару слов и ему. Он содержит единственный метод Dispose(), который нужен для освобождения ресурсов. Каждый раз, по завершении цикла или при выходе из него по другим причинам IEnumirator очищает ресурсы.

Давайте посмотрим на такой цикл:

1
2
3
4
5
6
7
8
9
System.Collections.Generic.Queue < int > queue = new System.Collections.Generic.Queue< int >();
 queue.Enqueue(1);
 queue.Enqueue(2);
 queue.Enqueue(3);

 foreach (int item in queue)
 {
 Console.WriteLine(item);
 }

Компилятор преобразует его в подобный код:

1
2
3
4
5
6
7
8
9
10
11
System.Collections.Generic.Queue< int > queue = new System.Collections.Generic.Queue< int >();
 queue.Enqueue(1);
 queue.Enqueue(2);
 queue.Enqueue(3);

int num;
while (queue.MoveNext())
{
 num = queue.Current;
 Console.WriteLine(num);
}

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

Но, все же этот код лишь приближен к тому что действительно выдаст компилятор. Проблема в том, что если у вас два и более пересекающихся циклов, работающих с одной коллекцией, то каждый вызов MoveNext будет влиять на все циклы. Такой ход событий не никого не устроит. И поэтому придумали второй интерфейс IEnumirator.

Он содержит единственный метод GetEnumerator(), который возвращает перечислитель. Таким образом IEnumerable и его обобщенная версия IEnumerable позволяют вынести логику перечисления элементов из класса коллекции. Обычно это вложенный класс, который имеет доступ к элементам коллекции и поддерживает IEnumerator. Имея каждый свой перечислитель, разные потребители не будут мешать друг другу, выполняя перечисление коллекции одновременно.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
System.Collections.Generic.Queue< int > queue = new System.Collections.Generic.Queue< int >();
 System.Collections.Generic.Queue< int >.Enumerator enumirator;

 IDisposable disposable;

 enumirator = queue.GetEnumerator();
 queue.Enqueue(1);
 queue.Enqueue(2);
 queue.Enqueue(3);

 try
 {
 int num;
 while (enumirator.MoveNext())
 {
 num = enumirator.Current;
 Console.WriteLine(num);
 }
 }
 finally
 {
 disposable = (IDisposable)enumirator;
 disposable.Dispose();
 }

Наверное, вы думаете, что для итерации коллекции необходимо реализовать интерфейсы IEnumerable и IEnumerable. Однако это не совсем так. Для компиляции foreach вам достаточно, чтобы объект реализовывал метод GetEnumerator(), который вернет другой объект со свойством Current и методом MoveNext().

Здесь применяется утиная типизация - известный подход:

"Если что-то ходит, как утка, и крякает, как утка, то это утка".

То есть, если есть объект с методом GetEnumerator(), который возвращает объект с методом MoveNext() и свойством Current, то это и есть перечислитель.

В противном случае, если нужные объекты, с нужными методами не найдены, будут искаться интерфейсы IEnumerable и IEnumerable.

Таким образом foreach является действительно универсальным циклом, который отлично работает как с массивами, так и с коллекциями. Я использую его постоянно. Однако, несть один минус у foreach - он позволяет только читать элементы, и не позволяет их изменять. Поэтому старый добрый for никогда не пропадет из нашего кода.

avatar
Dmitriy Fedyashov
Руководитель отдела обеспечения качества
.NET C#

Добавить комментарий
logo
  • +7(800)551-75-80
  • info@fastreport.ru
  • Ростов-на-Дону, Россия, 344082, ул.Обороны 24, офис 311
  • Купить
  • Загрузить
  • Документация
  • Отзывы
  • Как деинсталировать
  • Онлайн поддержка
  • FAQ
  • Видео уроки
  • Форум
  • Статьи
  • Наши Новости
  • Пресса о нас
  • Реселлеры
  • ВУЗам
  • Наша команда
  • Карьера
  • Контакты

© 1998-2021 ООО «Фаст Репортс»

  • Согласие с обработкой персональных данных
  • Не является публичной офертой