Может ли кто-нибудь объяснить мне IEnumerable и IEnumerator?

может ли кто-нибудь объяснить мне IEnumerable и IEnumerator?

например, когда использовать его над foreach? в чем разница между IEnumerable и IEnumerator? Зачем нам его использовать?

14 ответов


например, когда использовать его над foreach?

вы не используете IEnumerable "на" foreach. Реализация IEnumerable использовать foreach возможно.

когда вы пишете код типа:

foreach (Foo bar in baz)
{
   ...
}

это функционально эквивалентно записи:

IEnumerator bat = baz.GetEnumerator();
while (bat.MoveNext())
{
   bar = (Foo)bat.Current
   ...
}

под "функционально эквивалентным" я имею в виду, что это на самом деле то, во что компилятор превращает код. Вы не можете использовать foreach on baz в этом примере если baz осуществляет IEnumerable.

IEnumerable означает, что baz реализует метод

IEnumerator GetEnumerator()

на IEnumerator объект, который возвращает этот метод, должен реализовать методы

bool MoveNext()

и

Object Current()

первый метод переходит к следующему объекту в IEnumerable объект, который создал перечислитель, возвращающий false если это сделано, а второй возвращает текущий объект.

что-нибудь в .Net, который вы можете перебирать над implements IEnumerable. Если вы создаете свой собственный класс, и он еще не наследуется от класса, который реализует IEnumerable, вы можете сделать свой класс полезная в foreach отчетность по реализации IEnumerable (и путем создания класса перечислителя, который его новый GetEnumerator метод возвращает).


интерфейсы IEnumerable и IEnumerator

чтобы начать изучение процесса реализации существующих интерфейсов .NET, давайте сначала рассмотрим роль IEnumerable и IEnumerator. Напомним, что C# поддерживает ключевое слово foreach, который позволяет перебирать содержимое любого типа массива:

// Iterate over an array of items.
int[] myArrayOfInts = {10, 20, 30, 40};
foreach(int i in myArrayOfInts)
{
   Console.WriteLine(i);
}

хотя может показаться, что только типы массивов могут использовать эту конструкцию, истина в том, что любой тип, поддерживающий метод с именем GetEnumerator () может быть оценен конструкцией foreach.К иллюстрации, за мной!

Предположим у нас есть класс гаража:

// Garage contains a set of Car objects.
public class Garage
{
   private Car[] carArray = new Car[4];
   // Fill with some Car objects upon startup.
   public Garage()
   {
      carArray[0] = new Car("Rusty", 30);
      carArray[1] = new Car("Clunker", 55);
      carArray[2] = new Car("Zippy", 30);
      carArray[3] = new Car("Fred", 30);
   }
}

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

// This seems reasonable ...
public class Program
{
   static void Main(string[] args)
   {
      Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****\n");
      Garage carLot = new Garage();
      // Hand over each car in the collection?
      foreach (Car c in carLot)
      {
         Console.WriteLine("{0} is going {1} MPH",
         c.PetName, c.CurrentSpeed);
      }
      Console.ReadLine();
   }
}

к сожалению, компилятор сообщает вам, что класс Garage не реализует метод с именем Метод getenumerator(). Этот метод формализован интерфейсом IEnumerable, который обнаружен в системе.Коллекции пространства имен. Классы или структуры, поддерживающие это поведение, объявляют, что они могут предоставлять содержащиеся подэлементы вызывающему объекту (в этом примере само ключевое слово foreach). Вот определение этого стандартного интерфейса .NET:

// This interface informs the caller
// that the object's subitems can be enumerated.
public interface IEnumerable
{
   IEnumerator GetEnumerator();
}

как вы можете видеть, метод GetEnumerator() возвращает ссылку на еще один интерфейс с именем Система.Коллекции.Интерфейс IEnumerator. Этот интерфейс предоставляет инфраструктуру, позволяющую вызывающему объекту для обхода внутренних объектов, содержащихся в контейнере, совместимом с IEnumerable:

// This interface allows the caller to
// obtain a container's subitems.
public interface IEnumerator
{
   bool MoveNext (); // Advance the internal position of the cursor.
   object Current { get;} // Get the current item (read-only property).
   void Reset (); // Reset the cursor before the first member.
}

если вы хотите обновить тип гаража для поддержки этих интерфейсов, вы можете пройти долгий путь и реализуйте каждый метод вручную. Пока вы определенно свободны обеспечить подгонянные версии GetEnumerator (), MoveNext (), Current и Reset (), есть более простой способ. Как система.Тип массива (а также многие другие классы коллекций) уже реализует IEnumerable и IEnumerator, вы можете просто делегировать запрос системе.Массив следующим образом:

using System.Collections;
...
public class Garage : IEnumerable
{
   // System.Array already implements IEnumerator!
   private Car[] carArray = new Car[4];
   public Garage()
   {
      carArray[0] = new Car("FeeFee", 200);
      carArray[1] = new Car("Clunker", 90);
      carArray[2] = new Car("Zippy", 30);
      carArray[3] = new Car("Fred", 30);
   }
   public IEnumerator GetEnumerator()
   {
      // Return the array object's IEnumerator.
      return carArray.GetEnumerator();
   }
}

после обновления типа гаража можно безопасно использовать тип в конструкции c# foreach. Кроме того, учитывая, что метод GetEnumerator() был определен публично, пользователь объекта также может взаимодействовать с типом IEnumerator:

// Manually work with IEnumerator.
IEnumerator i = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} is going {1} MPH", myCar.PetName, myCar.CurrentSpeed);

однако, если вы предпочитаете скрыть функциональность IEnumerable от уровня объекта, просто сделайте использование явного интерфейса реализация:

IEnumerator IEnumerable.GetEnumerator()
{
  // Return the array object's IEnumerator.
  return carArray.GetEnumerator();
}

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

адаптация Pro C# 5.0 и .NET 4.5 Framework


реализация IEnumerable означает, что ваш класс возвращает объект IEnumerator:

public class People : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        // return a PeopleEnumerator
    }
}

реализация IEnumerator означает, что ваш класс возвращает методы и свойства для итерации:

public class PeopleEnumerator : IEnumerator
{
    public void Reset()...

    public bool MoveNext()...

    public object Current...
}

это разница в любом случае.


объяснение по аналогии + код Пошаговое Руководство

сначала объяснение без кода, затем я добавлю его позже.

предположим, вы управляете авиакомпанией. И в каждом самолете вы хотите узнать информацию о пассажирах, летящих в самолете. В основном вы хотите иметь возможность "пересечь" самолет. Другими словами, вы хотите иметь возможность начать с переднего сиденья, а затем проложить свой путь к задней части самолета, запрашивая у пассажиров некоторую информацию: кто они, откуда и т. д. Самолет может сделать это, только если он:

  1. счетно, и
  2. если у него есть счетчик.

Если нет "счетчика", и если он не "счетный", вы не получите очень далеко. почему эти требования? потому что интерфейс требует.

что означает счетный?

Если авиакомпания "Счетная", это означает, что должен быть рейс присутствующий в самолете бортпроводник, единственная задача которого-считать - и этот бортпроводник должен считать очень специфическим образом (согласно законам гражданской авиации):

  1. счетчик / стюардесса должна начать до первого пассажира (на передней части каждого, где они демо безопасности, как надеть спасательный жилет и т.д.).
  2. он/она (т. е. стюардесса) должен "двигаться дальше" по проходу к первому месту.
  3. он / она должен затем записать: (i) кто человек находится на месте, и (ii) их текущее местоположение в проходе.

Процедуры Подсчета

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

таким образом, капитан всегда может получить информацию о текущем подследственного. Таким образом, если он узнает, что этому человеку нравится "Манчестер Сити", он может предоставить этому пассажиру преференциальное отношение и т. д.

  • счетчик продолжает идти, пока он не достигнет конца самолета.

давайте свяжем это с IEnumerables

  • перечисляемый просто сбор пассажиров в самолете. Закон О гражданской авиации-это в основном правила, которым должны следовать все IEnumerables. Каждый раз, когда служащий авиакомпании идет к капитану с информацией о пассажире, мы в основном "уступаем" пассажира капитану. Капитан может делать с пассажиром все, что ему заблагорассудится, за исключением перестановки пассажиров в самолете. В этом случае им предоставляется преференциальный режим, если они следуют Манчестер Сити (фу!)

    foreach (Passenger passenger in Plane)
    // the airline hostess is now at the front of the plane
    // and slowly making her way towards the back
    // when she get to a particular passenger she gets some information
    // about the passenger and then immediately heads to the cabin
    // to let the captain decide what to do with it
    { // <---------- Note the curly bracket that is here.
        // we are now cockpit of the plane with the captain.
        // the captain wants to give the passenger free 
        // champaign if they support manchester city
        if (passenger.supports_mancestercity())
        {
            passenger.getFreeChampaign();
        } else
        {
            // you get nothing! GOOD DAY SIR!
        }
    } //  <---- Note the curly bracket that is here!
          the hostess has delivered the information 
          to the captain and goes to the next person
          on the plane (if she has not reached the 
          end of the plane)
    

резюме

другими словами, что-то счетным, если оно установлен счетчик. И счетчик должен (в основном): (i) помнить свое место (государство), (II) быть в состоянии дальше, (iii) и знать о настоящее человек, с которым он имеет дело.

Enumerable-это просто причудливое слово для "Счетного". Другими словами, enumerable позволяет вам "перечислять" (т. е. счет).


IEnumerable реализует GetEnumerator. При вызове этот метод вернет IEnumerator который реализует MoveNext, сброс и ток.

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


реализация IEnumerable позволяет получить IEnumerator для списка.

IEnumerator обеспечивает последовательный доступ к элементам списка в стиле foreach с помощью ключевого слова yield.

перед реализацией foreach (например, в Java 1.4) способ итерации списка состоял в том, чтобы получить перечислитель из списка, а затем попросить его для "следующего" элемента в списке, пока значение, возвращаемое как следующий элемент, не равно null. Foreach просто делает это неявно как языковая функция, так же, как lock() реализует класс монитора за кулисами.

Я ожидаю, что foreach работает над списками, потому что они реализуют IEnumerable.


  • объект, реализующий IEnumerable позволяет другим посетить каждый из его пунктов (счетчиками).
  • объект, реализующий IEnumerator - это делает итерации. Это цикл над перечисляемым объектом.

подумайте о перечисляемых объектах как о списках, стеках, деревьях.


IEnumerable и IEnumerator (и их общие аналоги IEnumerable и IEnumerator) являются базовыми интерфейсами итератор реализации в коллекции Libray классов .Net Framework.

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

IEnumerator, С другой стороны, обеспечивает интерфейс итерации несколько более низкого уровня. Это называется явный итератор что дает программисту больше контроля над циклом итерации.

IEnumerable

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

цикл foreach

foreach (var value in list)
  Console.WriteLine(value);

Я думаю foreach loop является одной из основных причин использования IEnumerable интерфейсы. foreach имеет очень краткий синтаксис и очень легко понять по сравнению с классическим C стиль для петель, где вам нужно чтобы проверить различные переменные, чтобы увидеть, что он делает.

ключевое слово yield

вероятно, менее известной особенностью является то, что IEnumerable позволяет генераторы в C# С использованием yield return и yield break заявления.

IEnumerable<Thing> GetThings() {
   if (isNotReady) yield break;
   while (thereIsMore)
     yield return GetOneMoreThing();
}

абстракции

Другой распространенный сценарий на практике использует IEnumerable для обеспечения минималистичных абстракций. Потому что это крошечный и только для чтения интерфейс, вам рекомендуется выставлять свои коллекции как IEnumerable (а не список например). Таким образом, вы можете изменить свою реализацию, не нарушая код своего клиента (изменить список на LinkedList например).

понял

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

IEnumerator

IEnumerator, с другой стороны, является закулисным интерфейсом, который делает IEnumerble-foreach-магия работа. Строго говоря, он включает явные итераторы.

var iter = list.GetEnumerator();
while (iter.MoveNext())
    Console.WriteLine(iter.Current);

по моему опыту IEnumerator редко используется в общих сценариях из-за его более подробного синтаксиса и немного запутанной семантики (по крайней мере, для меня; например MoveNext () также возвращает значение, которое имя вообще не предлагает).

использовать чехол для IEnumerator

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

клиентский код

foreach(var item in feed.GetItems())
    Console.WriteLine(item);

библиотека

IEnumerable GetItems() {
    return new FeedIterator(_fileNames)
}

class FeedIterator: IEnumerable {
    IEnumerator GetEnumerator() {
        return new FeedExplicitIterator(_stream);
    }
}

class FeedExplicitIterator: IEnumerator {
    DataItem _current;

    bool MoveNext() {
        _current = ReadMoreFromStream();
        return _current != null;           
    }

    DataItem Current() {
        return _current;   
    }
}

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

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

большую часть времени IEnumerable & IEnumerator используются прозрачно как часть foreach петли.


различия между IEnumerable и IEnumerator:

  • IEnumerable использует IEnumerator внутренне.
  • IEnumerable не знает, какой элемент / объект выполняется.
  • всякий раз ,когда мы передаем IEnumerator другой функции, он знает текущее положение элемента/объекта.
  • всякий раз, когда мы передаем коллекцию IEnumerable другой функции, это не знает текущей позиции элемента / объекта(не знает, какой элемент его executing)

    IEnumerable имеют один метод GetEnumerator ()

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

IEnumerator имеет одно свойство current и два метода Reset и MoveNext(что полезно для знания текущей позиции элемента в списке).

public interface IEnumerator
{
     object Current { get; }
     bool MoveNext();
     void Reset();
}

понимание шаблона итератора будет полезно для вас. Я рекомендую прочитать то же самое.

Итератор Шаблон

на высоком уровне шаблон итератора может использоваться для обеспечения стандартного способа итерации по коллекциям любого типа. У нас есть 3 участника в шаблоне итератора, фактическая коллекция (клиент), агрегатор и итератор. В совокупности-это интерфейс/абстрактный класс, который имеет метод, который возвращает итератор. Iterator-это интерфейс / абстрактный класс, который имеет методы, позволяющие нам перебирать коллекцию.

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

вот диаграмма UML Iterator Pattern

так в основном в c#, IEnumerable-абстрактная совокупность, а IEnumerator-абстрактный итератор. IEnumerable имеет один метод GetEnumerator, который отвечает за создание экземпляра IEnumerator нужного типа. Коллекции, подобные спискам, реализуют IEnumerable.

пример. Предположим, что у нас есть метод getPermutations(inputString) что возвращает все перестановки строки и что метод возвращает экземпляр IEnumerable<string>

для того, чтобы подсчитать количество перестановок мы могли бы сделайте что-нибудь вроде этого.

 int count = 0;
        var permutations = perm.getPermutations(inputString);
        foreach (string permutation in permutations)
        {
            count++;
        }

компилятор c# более или менее преобразует вышеуказанное в

using (var permutationIterator = perm.getPermutations(input).GetEnumerator())
        {
            while (permutationIterator.MoveNext())
            {
                count++;
            }
        }

если у вас есть какие-либо вопросы пожалуйста, не стесняйтесь спрашивать.


незначительный вклад.

как многие из них объясняют о "когда использовать" и "использовать с foreach". Я подумал о том, чтобы добавить еще один Разница Государства здесь, как указано в вопросе о разнице между IEnumerable и IEnumerator.

Я создал приведенный ниже пример кода на основе приведенных ниже потоков обсуждения.

IEnumerable, IEnumerator против foreach, когда использовать что в чем разница между IEnumerator и IEnumerable?

Enumerator сохраняет состояние (позицию итерации) между вызовами функций, в то время как итерации другой стороны Enumerable не делает.

вот проверенный пример с комментариями, чтобы понять.

эксперты, пожалуйста, добавьте/поправьте меня.

static void EnumerableVsEnumeratorStateTest()
{
    IList<int> numList = new List<int>();

    numList.Add(1);
    numList.Add(2);
    numList.Add(3);
    numList.Add(4);
    numList.Add(5);
    numList.Add(6);

    Console.WriteLine("Using Enumerator - Remembers the state");
    IterateFrom1to3(numList.GetEnumerator());

    Console.WriteLine("Using Enumerable - Does not Remembers the state");
    IterateFrom1to3Eb(numList);

    Console.WriteLine("Using Enumerable - 2nd functions start from the item 1 in the collection");
}

static void IterateFrom1to3(IEnumerator<int> numColl)
{
    while (numColl.MoveNext())
    {
        Console.WriteLine(numColl.Current.ToString());

        if (numColl.Current > 3)
        {
            // This method called 3 times for 3 items (4,5,6) in the collection. 
            // It remembers the state and displays the continued values.
            IterateFrom3to6(numColl);
        }
    }
}

static void IterateFrom3to6(IEnumerator<int> numColl)
{
    while (numColl.MoveNext())
    {
        Console.WriteLine(numColl.Current.ToString());
    }
}

static void IterateFrom1to3Eb(IEnumerable<int> numColl)
{
    foreach (int num in numColl)
    {
        Console.WriteLine(num.ToString());

        if (num>= 5)
        {
            // The below method invokes for the last 2 items.
            //Since it doesnot persists the state it will displays entire collection 2 times.
            IterateFrom3to6Eb(numColl);
        }
    }
}

static void IterateFrom3to6Eb(IEnumerable<int> numColl)
{
    Console.WriteLine();
    foreach (int num in numColl)
    {
        Console.WriteLine(num.ToString());
    }
}

Я заметил эти различия:

A. Мы повторяем список по-разному, foreach может использоваться для IEnumerable и while loop для IEnumerator.

B. IEnumerator может запомнить текущий индекс, когда мы переходим от одного метода к другому (он начинает работать с текущим индексом), но IEnumerable не может вспомнить индекс, и он сбрасывает индекс в начало. Подробнее в этом видео https://www.youtube.com/watch?v=jd3yUjGc9M0


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Enudemo
{

    class Person
    {
        string name = "";
        int roll;

        public Person(string name, int roll)
        {
            this.name = name;
            this.roll = roll;
        }

        public override string ToString()
        {
            return string.Format("Name : " + name + "\t Roll : " + roll);
        }

    }


    class Demo : IEnumerable
    {
        ArrayList list1 = new ArrayList();

        public Demo()
        {
            list1.Add(new Person("Shahriar", 332));
            list1.Add(new Person("Sujon", 333));
            list1.Add(new Person("Sumona", 334));
            list1.Add(new Person("Shakil", 335));
            list1.Add(new Person("Shruti", 336));
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
           return list1.GetEnumerator();
        }
    }



    class Program
    {
        static void Main(string[] args)
        {
            Demo d = new Demo();  // Notice here. it is simple object but for 
                                //IEnumerator you can get the collection data

            foreach (Person X in d)
            {
                Console.WriteLine(X);
            }

            Console.ReadKey();
        }
    }
}
/*
Output : 

Name : Shahriar  Roll : 332
Name : Sujon     Roll : 333
Name : Sumona    Roll : 334
Name : Shakil    Roll : 335
Name : Shruti    Roll : 336
  */