Добавлен Msey,
опубликован
Раздел:
C#
Итератор — это объект, позволяющий перемещаться (итерироваться) по элементам некоторой последовательности. По природе своей итератор — это поведенческий шаблон проектирования.
Почти каждой написанной вами программе придется выполнять итерацию определенной коллекции. Для этого вы напишете код, проверяющий каждый элемент в коллекции.
Кроме того, вы создадите методы итератора — методы, которые итератор создает для элементов соответствующего класса. Их можно использовать в следующих целях:
• Выполнение определенного действия с каждым элементом в коллекции.
• Перечисление настраиваемой коллекции.
• Расширение LINQ или других библиотек.
• Создание конвейера данных, обеспечивающего эффективный поток данных через методы итератора.
• Перечисление настраиваемой коллекции.
• Расширение LINQ или других библиотек.
• Создание конвейера данных, обеспечивающего эффективный поток данных через методы итератора.
К примеру, у вас есть коллекция, которая постоянно заполняется данными, но нам необходимо извлечь из нее содержимое по заданным критериям. Вариантов это сделать несколько, но сейчас разберем примеры с итераторами.
Для начала создадим тестовую площадку с примитивным индексируемым объектом:
public class Item
{
private readonly int id;
public int Id => id;
public Item(int id) => this.id = id;
public void Print() => Console.WriteLine(id);
}
Если все сделано правильно, то на консоли будет единица при запуске такого метода в приложении:
static void Main()
{
var example = new Item(1);
example.Print();
Console.Read();
}
Теперь можем сделать метод Main так, чтобы можно было перебрать все элементы списка с последующим выводом их индексов на консоли:
static void Main()
{
List<Item> example = new List<Item>
{
new Item(-2),
new Item(-1),
new Item(0),
new Item(1),
new Item(2)
};
foreach (Item item in example)
item.Print();
Console.Read();
}
Вывод: -2 -1 0 1 2
foreach
Оператор foreach разворачивается в стандартную идиому, которая выполняет итерацию всех элементов в коллекции с помощью интерфейсов IEnumerable<T> и IEnumerator<T>. Кроме того, он сводит к минимуму ошибки, допускаемые разработчиками в результате неправильного управления ресурсами.
Компилятор преобразует цикл foreach, показанный в первом примере, в конструкцию следующего вида:
(C# 5.0)
(C# 5.0)
IEnumerator<int> enumerator = collection.GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
item.Print();
}
Другими словами каждый объект, который наследуется и реализовывает интерфейс IEnumerable, может использоваться с оператором foreach
yield retun / break / wtf?
yield — ключевое слово, которое своим присутствием в методе / операторе / аксессоре означает принадлежность к итератору. Иными словами все, в чем находится данное ключевое слово, и есть итератор.
Самый простой пример, который заменяет коллекцию из нашего приложения на итератор с ключевым словом yield:
public static IEnumerable<Item> GetAllItems()
{
yield return new Item(-2);
yield return new Item(-1);
yield return new Item(0);
yield return new Item(1);
yield return new Item(2);
}
static void Main()
{
IEnumerable<Item> example = GetAllItems();
foreach (Item item in example)
item.Print();
Console.Read();
}
И аналогичным результатом будет: -2 -1 0 1 2
Оператор yield return используется для возврата каждого элемента по одному.
Последовательность, которая возвращается после выполнения метода итератора, можно использовать с помощью оператора foreach или запроса LINQ. Каждая итерация цикла foreach вызывает метод итератора. При достижении в методе итератора оператора yield return возвращается expression и сохраняется текущее расположение в коде. При следующем вызове функции итератора выполнение возобновляется с этого места.
Для завершения итерации можно использовать оператор yield break.
Ниже приведен полноценный пример работы итератора.
Мы создаем объект, в конструкторе которого с вероятностью 40% может создастся два дочерних объекта с номером индекса +100 и +101 от родительского. Наша задача - вывести все индексы всех дочерних объектов на консоль:
using System;
using System.Collections.Generic;
class Program
{
public class Item
{
private readonly int id;
public int Id => id;
public Item(int id)
{
this.id = id;
Children = new List<Item>();
int chance = new Random().Next(0, 100);
if (chance <= 40) // шанс срабатывания на добавления двух "детей"
{
Children.Add(new Item(id + 100));
Children.Add(new Item(id + 101));
}
}
public void Print() => Console.WriteLine(id);
List<Item> Children;
public IEnumerable<Item> GetChildren() // итератор, собирающих посредством yield return все дочерние элементы
{
if (Children.Count <= 0) yield break;
foreach (var child in Children)
{
yield return child; // просто возвращаем дочерние элементы
// мы не забыли о том, что у дочерних элементов тоже может быть два дочерних элемента, что обрабатывается ниже
foreach (var subchild in child.GetChildren()) // рекурсивно вызываем метод аггрегации дочерних элементов у текущих дочерних элементов
{
yield return subchild;
}
}
}
}
static void Main()
{
foreach (Item item in new Item(0).GetChildren())
item.Print();
Console.Read();
}
}
Исключения:
Оператор yield return нельзя размещать в блоке try-catch. Оператор yield return можно размещать в блоке try оператора try-finally.
Оператор yield break можно размещать в блоке try или catch, но не в блоке finally.
Если тело оператора foreach (вне метода итератора) вызывает исключение, выполняется блок finally в методе итератора.
За сим все. Спасибо за внимание
`
ОЖИДАНИЕ РЕКЛАМЫ...
2
Bornikkeny
4 года назад
2
Хорошая статья
2
Msey
4 года назад
2
Если кому не сложно, то напишите, какие бы статьи были бы вам интересны. Пока есть свободное время, попробую написать парочку
2
Bornikkeny
4 года назад
2
Асинхронность на .net 4.0 и 4.5> очень будет интересна
0
Nerevar
4 года назад
0
На мой взгляд стоит добавить к описанию оператора yield пример того, во что он превращается на самом деле, как [здесь]
0
PornoMishka
4 года назад
0
+1 за асинхронность
Чтобы оставить комментарий, пожалуйста, войдите на сайт.