Почему нам нужны итераторы в c#?

Для песочниц онлайн для нескольких различных языков платформы и приложения с открытым исходным кодом, видят это подобное ТАК вопрос .

11
задан Bryan Denny 4 August 2009 в 12:55
поделиться

10 ответов

Итераторы - это абстракция, которая отделяет понятие позиции в коллекции от самой коллекции . Итератор - это отдельный объект, хранящий состояние, необходимое для поиска элемента в коллекции и перехода к следующему элементу в коллекции. Я видел коллекции, которые сохраняли это состояние внутри коллекции (то есть текущую позицию), но часто лучше переместить это состояние на внешний объект. Помимо прочего, он позволяет иметь несколько итераторов, повторяющих одну и ту же коллекцию.

12
ответ дан 3 December 2019 в 01:00
поделиться

Вы, наверное, слышали о массивы и контейнеры - объекты, в которых хранится список других объектов.

Но для того, чтобы объект представлял список, ему на самом деле не нужно «хранить» список. Все, что ему нужно сделать, это предоставить вам методы или свойства, которые позволят вам получить элементы списка.

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

Чтобы немного упростить это (не считая исторического багажа):

public interface IEnumerable<T>
{
    IEnumerator<T> GetEnumerator();
}

Итак, вы можете получить из него счетчик. Этот интерфейс (опять же, немного упрощенный, чтобы убрать отвлекающий шум):

public interface IEnumerator<T>
{
    bool MoveNext();
    T Current { get; }
}

Итак, чтобы просмотреть список, вы должны сделать следующее:

var e = list.GetEnumerator();
while (e.MoveNext())
{
    var item = e.Current;

    // blah
}

Этот шаблон аккуратно захватывается ключевым словом foreach :

foreach (var item in list)
    // blah

А как насчет создания списка нового типа? Да, мы можем просто использовать List и заполнить его элементами. Но что, если мы хотим обнаруживать предметы «на лету» по запросу? В этом есть преимущество, заключающееся в том, что клиент может отказаться от итерации после первых трех элементов, и ему не нужно «оплачивать стоимость» генерации всего списка.

Для реализации такого типа ленивого списка вручную было бы хлопотно. Нам пришлось бы написать два класса, один для представления списка путем реализации IEnumerable , а другой для представления активной операции перечисления путем реализации IEnumerator .

Методы итератора выполняют всю тяжелую работу для нас. Мы просто пишем:

IEnumerable<int> GetNumbers(int stop)
{
    for (int n = 0; n < stop; n++)
        yield return n;
}

И компилятор преобразует это за нас в два класса. Вызов метода эквивалентен созданию объекта класса, представляющего список.

16
ответ дан 3 December 2019 в 01:00
поделиться

Простой пример: функция, которая генерирует последовательность целых чисел:

static IEnumerable<int> GetSequence(int fromValue, int toValue)
{
    if (toValue >= fromValue)
    {
        for (int i = fromValue; i <= toValue; i++)
        {
            yield return i;
        }
    }
    else
    {
        for (int i = fromValue; i >= toValue; i--)
        {
            yield return i;
        }
    }
}

Чтобы сделать это без итератора, вам нужно будет создать массив и перечислить его ...

6
ответ дан 3 December 2019 в 01:00
поделиться

Итерирует студентов в классе

Шаблон проектирования итератора обеспечивает нас с общим методом перечисления список элементов или массив при скрытии детали списка реализация. Это обеспечивает более чистое использование объекта массива и скрывает ненужную информацию от клиент, что в конечном итоге приводит к улучшенное повторное использование кода, улучшенное ремонтопригодность и меньше ошибок. В шаблон итератора может перечислять список предметов независимо от их фактический тип хранилища.

4
ответ дан 3 December 2019 в 01:00
поделиться

Выполните итерацию по набору вопросов для домашнего задания.

А если серьезно, то итераторы могут предоставить унифицированный способ обхода элементов в коллекции независимо от базовой структуры данных.

Прочтите первое. два параграфа здесь для получения дополнительной информации.

3
ответ дан 3 December 2019 в 01:00
поделиться

Они отлично подходят для пары вещей:
a) Для «воспринимаемой производительности» при сохранении аккуратности кода - итерация чего-либо, отделенного от другой логики обработки.
б) Когда количество элементов, которые вы собираетесь перебирать, неизвестно.

Хотя и то, и другое можно сделать другими способами, с помощью итераторов код можно сделать лучше и аккуратнее, поскольку кому-то, вызывающему итератор, не нужно беспокоиться о том, как он находит материал для итерации ...

Пример из реальной жизни: перечисление каталогов и файлов и поиск первого [n], который удовлетворяет некоторым критериям, например, файл, содержащий определенную строку или последовательность и т. д.

2
ответ дан 3 December 2019 в 01:00
поделиться

Помимо всего прочего, для перебора последовательностей ленивого типа - IEnumerators. Каждый следующий элемент такой последовательности может быть оценен / инициализирован на шаге итерации, что позволяет перебирать бесконечные последовательности с использованием конечного количества ресурсов ...

2
ответ дан 3 December 2019 в 01:00
поделиться

Итератор - это простой способ реализации интерфейса IEnumerator. Вместо того, чтобы создавать класс, который имеет методы и свойства, необходимые для интерфейса, вы просто создаете метод, который возвращает значения одно за другим, а компилятор создает класс с методами и свойствами, необходимыми для реализации интерфейса.

Если вы например, у вас есть большой список чисел, и вы хотите вернуть коллекцию, в которой каждое число умножается на два, вы можете создать итератор, который возвращает числа вместо создания копии списка в памяти:

public IEnumerable<int> GetDouble() {
   foreach (int n in originalList) yield return n * 2;
}

В C # 3 вы можете сделать нечто подобное, используя методы расширения и лямбда-выражения:

originalList.Select(n => n * 2)

Или используя LINQ:

from n in originalList select n * 2
1
ответ дан 3 December 2019 в 01:00
поделиться

Каноническим и простейшим примером является то, что он делает возможными бесконечные последовательности без сложности написания класса, чтобы сделать это самостоятельно:

// generate every prime number
public IEnumerator<int> GetPrimeEnumerator()
{
    yield return 2;
    var primes = new List<int>();
    primesSoFar.Add(2);
    Func<int, bool> IsPrime = n => primes.TakeWhile(
        p => p <= (int)Math.Sqrt(n)).FirstOrDefault(p => n % p == 0) == 0;

    for (int i = 3; true; i += 2)
    {
        if (IsPrime(i))
        {
            yield return i;
            primes.Add(i);
        }
    }
}

Очевидно, это не будет действительно бесконечным, если вы не используете BigInt вместо int, но это дает вам идею. Написание этого (или аналогичного) кода для каждой сгенерированной последовательности было бы утомительным и подверженным ошибкам. итераторы делают это за вас. Если приведенный выше пример кажется вам слишком сложным, подумайте:

// generate every power of a number from start^0 to start^n
public IEnumerator<int> GetPowersEnumerator(int start)
{   
    yield return 1; // anything ^0 is 1
    var x = start;
    while(true)
    {
        yield return x;
        x *= start;
    }      
}

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

Записывать перечисления над сложными структурами, такими как деревья и графы объектов, намного проще, поскольку обслуживание состояния в значительной степени выполняется за вас, вы должны просто напишите код для посещения каждого элемента и не беспокойтесь о том, чтобы вернуться к нему.


  1. Я не Используйте это слово легкомысленно - итерация O (n) может превратиться в O (N ^ 2)
2
ответ дан 3 December 2019 в 01:00
поделиться
IEnumerator<Question> myIterator = listOfStackOverFlowQuestions.GetEnumerator();
while (myIterator.MoveNext())
{
  Question q;
  q = myIterator.Current;
  if (q.Pertinent == true)
     PublishQuestion(q);
  else
     SendMessage(q.Author.EmailAddress, "Your question has been rejected");
}


foreach (Question q in listOfStackOverFlowQuestions)
{
    if (q.Pertinent == true)
        PublishQuestion(q);
    else    
        SendMessage(q.Author.EmailAddress, "Your question has been rejected");
}
1
ответ дан 3 December 2019 в 01:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: