Хтось може мені пояснити IEnumerable та IEnumerator? [зачинено]


272

Хтось може мені пояснити IEnumerable та IEnumerator?

наприклад, коли його використовувати над foreach? яка різниця між IEnumerable та IEnumerator? Навіщо нам це використовувати?


55
Це найгірша суміш імен з Java та JavaScript
Matthew Lock

@MatthewLock ви можете пояснити, що ви маєте на увазі?
Девід Клепмпнер

Відповіді:


274

наприклад, коли використовувати його над foreach?

Ви не використовуєте IEnumerable"понад" foreach. Реалізація IEnumerableробить foreach можливим використання .

Коли ви пишете код, наприклад:

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

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

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

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

IEnumerableозначає, що bazреалізує метод

IEnumerator GetEnumerator()

IEnumeratorОб'єкт, цей метод повертає повинні реалізувати методи

bool MoveNext()

і

Object Current()

Перший метод переходить до наступного об'єкта в IEnumerableоб'єкті, який створив перелік, повертаючись, falseякщо це зроблено, а другий повертає поточний об'єкт.

Що б там не було. Зверніть увагу, що ви можете переглядати програми IEnumerable. Якщо ви будуєте свій власний клас, і він вже не успадковується від класу, який реалізує IEnumerable, ви можете зробити свій клас придатним для використання в foreachоператорах, реалізуючи IEnumerable(і створивши клас перелічувача, який GetEnumeratorповерне його новий метод).


15
Я думаю, що коли оригінальний плакат сказав "над foreach", він мав на увазі "коли я повинен викликати GetEnumerator () / MoveNext () замість використання циклу foreach". За свою ціну.
mqp

6
Ах, я думаю, ти маєш рацію. Що ж, відповідь чітко міститься в моєму дописі: це не має значення, оскільки це все одно, що робить компілятор. Тому робіть те, що найпростіше, тобто використовуйте foreach.
Роберт Россні

12
Ця відповідь не розповідає всієї історії. Численні об'єкти не потребують реалізації IEnumerable; їм потрібно лише мати метод GetEnumerator, який повертає екземпляр типу, який, у свою чергу, має bool MoveNext()метод та Currentвластивість будь-якого типу. Такий підхід «набору качок» був реалізований в C # 1.0 як спосіб уникнути боксу при перерахуванні колекцій типу цінностей.
фог

3
Якщо висловлювання зверху і працює через Workthrough: Реалізація IEnumerable of (T), у вас є джерела живлення.
ruedi

4
Це такий тип, як C ++ iterator?
Метт Г

161

Інтерфейси 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);
   }
}

В ідеалі було б зручно перебирати підпункти об'єкта Garage за допомогою конструкції 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, який виявляється хованим у просторі імен System.Collections. Класи або структури, що підтримують таку поведінку, рекламують, що вони здатні викривати містять підпункти для абонента (у цьому прикладі саме ключове слово foreach). Ось визначення цього стандартного інтерфейсу .NET:

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

Як бачите, метод GetEnumerator () повертає посилання на ще один інтерфейс під назвою System.Collections.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.
}

Якщо ви хочете оновити тип Garage для підтримки цих інтерфейсів, ви можете пройти довгу дорогу та реалізувати кожен метод вручну. Хоча ви, безперечно, можете надати спеціалізовані версії GetEnumerator (), MoveNext (), Current і Reset (), є більш простий спосіб. Оскільки тип System.Array (як і багато інших класів колекції) вже реалізує IEnumerable та IEnumerator, ви можете просто делегувати запит System.Array таким чином:

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();
   }
}

Після оновлення типу Garage ви можете сміливо використовувати тип у конструкції forex C #. Крім того, враховуючи, що метод 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 () Garage, тоді як конструкція foreach отримає інтерфейс у фоновому режимі при необхідності.

Адаптовано з Pro C # 5.0 та .NET 4.5 Framework


1
Фантастична відповідь, дякую! Але в мене є одне питання. У першому прикладі ви перебираєте масив за допомогою foreach, але потім у другому прикладі цього не можете зробити. Це тому, що масив знаходиться в класі, або тому, що він містить об'єкти?
Caleb Palmquist

Дякую за чудове пояснення. Моє єдине питання - чому б просто не мати метод, Garageякий отримує carArray? Таким чином, вам не доведеться реалізовувати GetEnumeratorсебе, оскільки це Arrayвже робиться. Напр.foreach(Car c in carLot.getCars()) { ... }
Нік Роландо

63

Реалізація 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
Приємно, це пояснює (разом з іншими публікаціями), як перетворити свій клас в такий, який ви можете використовувати "foreach" для перегляду його вмісту.
Контанго

54

Пояснення за допомогою покрокової інструкції Analogy + Code

Спочатку пояснення без коду, потім я додам його згодом.

Скажімо, ви керуєте авіакомпанією. І в кожному літаку ви хочете знати інформацію про пасажирів, які літають в літаку. В основному, ви хочете мати можливість "пройти" літак. Іншими словами, ви хочете мати можливість стартувати на передньому сидінні, а потім пропрацювати свій шлях до задньої частини літака, запитуючи пасажирів деяку інформацію: хто вони, звідки вони і т. Д. Літак може це зробити лише , якщо це:

  1. лічильний, і
  2. якщо він має лічильник.

Чому ці вимоги? Тому що для цього потрібен інтерфейс.

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

Що означає лічильник?

Якщо авіакомпанія є "рахунковою", це означає, що В ПОВИННІЙ ПЛАНІ повинен бути присутнім стюардеса, яка єдина робота - це рахувати - і ця стюардеса ПОВИННА рахувати дуже конкретно:

  1. Стільниця / стюардеса ОБОВ'ЯЗКОВО запускаються перед першим пасажиром (перед усіма, де вони демонструють безпеку, як надягати рятувальний жилет тощо).
  2. Він / вона (тобто стюардеса) ОБОВ'ЯЗКОВО "рухаються далі" вгору по проходу до першого місця.
  3. Потім він / вона повинен записувати: (i) хто знаходиться на сидінні та (ii) їх поточне місцезнаходження у проході.

Процедури підрахунку

Капітан авіакомпанії хоче отримати звіт про кожного пасажира про те, як і коли вони розслідуються або підраховуються. Тож після розмови з людиною на першому сидінні стюардеса / стійка подає доповідь капітану, а коли дається звіт, лічильник запам'ятовує своє точне положення в проході і продовжує рахувати право, де він / вона пішов вимкнено.

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

  • Лічильник продовжує йти до тих пір, поки не досягне кінця літака.

Давайте зв’яжемо це з 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) і знати про поточну особу, з якою він має справу.

Численні - це просто фантазійне слово для "підрахунку". Іншими словами, число дозволяє вам «перерахувати» (тобто підрахувати).


23

IEnumerable реалізує GetEnumerator. При виклику цей метод поверне IEnumerator, який реалізує MoveNext, Reset та Current.

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


18

Реалізація IEnumerable дозволяє отримати список IEnumerator для списку.

IEnumerator дозволяє послідовно отримувати доступ до елементів у списку в стилі foreach, використовуючи ключове слово.

Перед реалізацією передбачення (наприклад, у Java 1.4) спосіб ітерації списку полягав у тому, щоб отримати перелік із списку, а потім задати йому пункт «наступний» у списку, доки значення повернеться як наступне елемент не є нульовим. Foreach просто робить це неявно, як мовна функція, так само, як lock () реалізує клас Monitor за кадром.

Я сподіваюся, що foreach працює за списками, оскільки вони реалізують IEnumerable.


якщо його для списку, чому я не можу просто використовувати foreach?
prodev42

.Net duck вводить заяву foreach, але IEnumerable / IEnumerable <T> є відповідним способом сказати, що ваш клас можна перерахувати.
user7116

15
  • Об'єкт, що реалізує IEnumerable, дозволяє іншим відвідувати кожен його елемент (за допомогою перелічувача) .
  • Об'єкт, що реалізує IEnumerator , виконує ітерацію. Це петля над численним об’єктом.

Подумайте про численні об’єкти, як про списки, стеки, дерева.


12

IEnumerable та IEnumerator (та їх загальні аналоги IEnumerable <T> та IEnumerator <T>) є базовими інтерфейсами реалізації ітераторів у колекціях бібліотек .Net Framework Class .

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

IEnumerator , з іншого боку, забезпечує трохи нижчий рівень інтерфейсу ітерації. Це називається явним ітератором, який дає програмісту більше контролю над циклом ітерації.

IE чимало

IEnumerable - це стандартний інтерфейс, який дозволяє повторювати колекції, які його підтримують (насправді всі типи колекцій, про які я сьогодні думаю, реалізують IEnumerable ). Підтримка компілятора дозволяє використовувати такі мовні функції foreach. Загалом, це дозволяє це неявна реалізація ітератора .

foreach Loop

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

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

дохідне слово

Можливо, менш відома особливість полягає в тому, що IEnumerable також дозволяє генераторам в C # за допомогою yield returnі yield breakоператорів.

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

Абстракції

Іншим поширеним сценарієм на практиці є використання IEnumerable для надання мінімалістичних абстракцій. Оскільки це інтерфейс, призначений лише для прочитання та лише для читання, вам пропонується виставити свої колекції як IEnumerable (а не Список, наприклад). Таким чином, ви можете змінити свою реалізацію, не порушуючи код клієнта (змінити, наприклад, Список на LinkedList ).

Готча

Слід пам’ятати про те, що в потокових реалізаціях (наприклад, завантаження даних за рядком у базу даних, замість того, щоб спочатку завантажувати всі результати в пам'ять), ви не можете повторювати колекцію не один раз. Це на відміну від колекцій у пам'яті, таких як List , де ви можете повторити кілька разів без проблем. Наприклад, ReSharper має перевірку коду для можливого багаторазового перерахування IEnumerable .

IEnumerator

З іншого боку, IEnumerator - це інтерфейс поза кадром, який робить магічну роботу IEnumerble-foreach . Власне кажучи, це дозволяє явні ітератори.

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

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

Використовуйте корпус для IEnumerator

Я використовував тільки IEnumerator, зокрема (трохи нижчий рівень) бібліотек та фреймворків, де я забезпечував інтерфейси 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;   
    }
}

9

Реалізація IEnumerableпо суті означає, що об'єкт може бути повторений. Це не обов'язково означає, що це масив, оскільки є певні списки, які не можна індексувати, але ви можете їх перерахувати.

IEnumerator- це власне об'єкт, який використовується для виконання ітерацій. Він контролює переміщення від одного об’єкта до іншого в списку.

Більшість випадків IEnumerable& IEnumeratorвикористовуються прозоро як частина foreachциклу.


6

Відмінності між IEnumerable та IEnumerator:

  • IEnumerable використовує внутрішньо IEnumerator.
  • IEnumerable не знає, який елемент / об’єкт виконує.
  • Кожного разу, коли ми передаємо IEnumerator іншій функції, він знає поточне положення елемента / об'єкта.
  • Кожного разу, коли ми передаємо колекцію IEnumerable до іншої функції, вона не знає поточного положення елемента / об'єкта (не знає, який елемент виконує)

    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();
}

5

IEnumerable - це вікно, яке містить Ienumerator. IEnumerable є базовим інтерфейсом для всіх колекцій. цикл foreach може працювати, якщо колекція реалізує IEnumerable. У наведеному нижче коді пояснюється крок створення власного перелік. Давайте спочатку визначимо наш клас, з якого ми збираємось скласти колекцію.

public class Customer
{
    public String Name { get; set; }
    public String City { get; set; }
    public long Mobile { get; set; }
    public double Amount { get; set; }
}

Тепер ми визначимо клас, який буде виступати як колекція для нашого класу. Зауважте, що він реалізує інтерфейс IEnumerable. Так що нам доведеться реалізувати метод GetEnumerator. Це поверне наш користувальницький перелік.

public class CustomerList : IEnumerable
{
    Customer[] customers = new Customer[4];
    public CustomerList()
    {
        customers[0] = new Customer { Name = "Bijay Thapa", City = "LA", Mobile = 9841639665, Amount = 89.45 };
        customers[1] = new Customer { Name = "Jack", City = "NYC", Mobile = 9175869002, Amount = 426.00 };
        customers[2] = new Customer { Name = "Anil min", City = "Kathmandu", Mobile = 9173694005, Amount = 5896.20 };
        customers[3] = new Customer { Name = "Jim sin", City = "Delhi", Mobile = 64214556002, Amount = 596.20 };
    }

    public int Count()
    {
        return customers.Count();
    }
    public Customer this[int index]
    {
        get
        {
            return customers[index];
        }
    }
    public IEnumerator GetEnumerator()
    {
        return customers.GetEnumerator(); // we can do this but we are going to make our own Enumerator
        return new CustomerEnumerator(this);
    }
}

Тепер ми збираємось створити власний власний Enumerator, як описано нижче. Отже, ми повинні реалізувати метод MoveNext.

 public class CustomerEnumerator : IEnumerator
    {
        CustomerList coll;
        Customer CurrentCustomer;
        int currentIndex;
        public CustomerEnumerator(CustomerList customerList)
        {
            coll = customerList;
            currentIndex = -1;
        }

        public object Current => CurrentCustomer;

        public bool MoveNext()
        {
            if ((currentIndex++) >= coll.Count() - 1)
                return false;
            else
                CurrentCustomer = coll[currentIndex];
            return true;
        }

        public void Reset()
        {
            // we dont have to implement this method.
        }
    }

Тепер ми можемо використовувати цикл foreach над нашою колекцією, як нижче;

    class EnumeratorExample
    {
        static void Main(String[] args)
        {

            CustomerList custList = new CustomerList();
            foreach (Customer cust in custList)
            {
                Console.WriteLine("Customer Name:"+cust.Name + " City Name:" + cust.City + " Mobile Number:" + cust.Amount);
            }
            Console.Read();

        }
    }

4

Розуміння структури Ітератора буде для вас корисним. Я рекомендую прочитати те саме.

Ітератор шаблон

На високому рівні схему ітератора можна використовувати для забезпечення стандартного способу ітерації за допомогою колекцій будь-якого типу. У нас є 3 учасники в ітераторному шаблоні, фактична колекція (клієнт), агрегатор та ітератор. Сукупність - це інтерфейс / абстрактний клас, який має метод, який повертає ітератор. Ітератор - це інтерфейс / абстрактний клас, який має методи, що дозволяють нам переглядати колекцію.

Для реалізації шаблону нам спочатку потрібно здійснити ітератор для отримання бетону, який може перебирати відповідну колекцію (клієнта). Потім колекція (клієнт) реалізує агрегатор, щоб повернути екземпляр зазначеного вище ітератора.

Ось схема UML Ітератор шаблон

Таким чином, в основному в 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++;
            }
        }

Якщо у вас є якісь питання, будь ласка, не соромтеся їх задавати.


2

Незначний внесок.

Оскільки багато хто з них пояснюють питання про те, «коли використовувати» та «використовувати з foreach». Я думав додати сюди ще одну різницю станів, як запитували питання про різницю між обома IEnumerable IEnumerator.

Я створив нижченаведений зразок коду на основі нижче обговорених тем.

IEnumerable, IEnumerator vs foreach, коли використовувати Яка різниця між IEnumerator та IEnumerable?

Керівник зберігає стан (позицію ітерації) між викликами функцій, тоді як ітерації, з іншого боку, перераховують.

Ось тестований приклад з коментарями, щоб зрозуміти.

Фахівці, будь ласка, додайте / виправте мене.

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());
    }
}

2

Я помітив ці відмінності:

A. Ми повторюємо список по-різному, foreach може використовуватися для IEnumerable і while цикл для IEnumerator.

B. IEnumerator може запам'ятати поточний індекс, коли ми переходимо від одного методу до іншого (він починає працювати з поточним індексом), але IEnumerable не може запам'ятати індекс і він скидає індекс до початку. Детальніше у цьому відео https://www.youtube.com/watch?v=jd3yUjGc9M0


1

IEnumerableі IEnumeratorобидва є інтерфейсами в C #.

IEnumerableце інтерфейс, що визначає єдиний метод, GetEnumerator()який повертає IEnumeratorінтерфейс.

Це працює для доступу лише до читання до колекції, яка реалізує, яку IEnumerableможна використовувати з foreachоператором.

IEnumeratorмає два способи, MoveNextі Reset. Він також має властивість під назвою Current.

Далі показано реалізацію IEnumerable та IEnumerator.


0
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
  */
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.