Czy ktoś może mi wyjaśnić IEnumerable i IEnumerator? [Zamknięte]

272

Czy ktoś może mi wyjaśnić IEnumerable i IEnumerator?

na przykład, kiedy użyć go za foreach? Jaka jest różnica między IEnumerable i IEnumerator? Dlaczego musimy go używać?

prodev42
źródło
55
To najgorszy mix nazewnictwa od czasów Java i JavaScript
Matthew Lock
@MatthewLock czy możesz wyjaśnić, co masz na myśli?
David Klempfner,

Odpowiedzi:

274

na przykład, kiedy użyć go za foreach?

Nie używasz IEnumerable„ponad” foreach. Wykonawcze IEnumerablesprawia, że korzystanie foreach to możliwe .

Kiedy piszesz kod taki jak:

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

jest funkcjonalnie równoważny z pisaniem:

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

Przez „funkcjonalnie równoważny” mam na myśli właśnie to, w co kompilator zamienia kod. Nie można używać foreachna bazw tym przykładzie chyba baz narzędzi IEnumerable.

IEnumerableoznacza, że bazimplementuje metodę

IEnumerator GetEnumerator()

IEnumeratorObiektem że metoda ta zwraca musi implementować metody

bool MoveNext()

i

Object Current()

Pierwsza metoda przechodzi do następnego obiektu w IEnumerableobiekcie, który utworzył moduł wyliczający, zwracając, falsejeśli jest zrobiony, a druga zwraca bieżący obiekt.

Wszystko w .Net, które można iterować po implementacjach IEnumerable. Jeśli budujesz własną klasę, która nie dziedziczy już po klasie, która się implementuje IEnumerable, możesz uczynić ją użyteczną w foreachinstrukcjach, implementując IEnumerable(i tworząc klasę wyliczającą, GetEnumeratorktóra zwróci jej nowa metoda).

Robert Rossney
źródło
15
Myślę, że kiedy oryginalny plakat powiedział „over foreach”, miał na myśli „kiedy powinienem jawnie wywołać GetEnumerator () / MoveNext () zamiast używać pętli foreach”. Tyle ile jest warte.
mqp
6
Ach, myślę, że masz rację. Cóż, odpowiedź jest niejawna w moim poście: to nie ma znaczenia, ponieważ i tak robi to kompilator. Więc rób to, co najłatwiejsze, tj. Używaj foreach.
Robert Rossney
12
Ta odpowiedź nie opowiada całej historii. Obiekty policzalne nie muszą implementować IEnumerable; muszą mieć tylko metodę GetEnumerator, która zwraca instancję typu, która z kolei ma bool MoveNext()metodę i Currentwłaściwość dowolnego typu. To podejście „typowania kaczego” zostało zaimplementowane w C # 1.0 jako sposób uniknięcia boksu podczas wyliczania kolekcji typów wartości.
phoog
3
Z wyjaśnieniem z góry i pracą przez Workthrough: Implementing IEnumerable of (T) masz dobre źródła.
ruedi
4
Czy to coś w stylu C ++ iterator?
Matt G
161

Interfejsy IEnumerable i IEnumerator

Aby rozpocząć badanie procesu wdrażania istniejących interfejsów .NET, najpierw przyjrzyjmy się roli IEnumerable i IEnumerator. Przypomnij sobie, że C # obsługuje słowo kluczowe o nazwie foreach, które pozwala iterować po zawartości dowolnego typu tablicy:

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

Chociaż może się wydawać, że tylko typy tablic mogą korzystać z tego konstruktu, prawda jest taka, że ​​każdy typ obsługujący metodę o nazwie GetEnumerator () może zostać oceniony przez konstrukcję foreach. Aby to zilustrować, podążaj za mną!

Załóżmy, że mamy klasę Garaż:

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

Idealnie byłoby iterować podelementy obiektu Garage za pomocą konstrukcji foreach, podobnie jak tablica wartości danych:

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

Niestety kompilator informuje, że klasa Garage nie implementuje metody o nazwie GetEnumerator (). Ta metoda jest sformalizowana przez interfejs IEnumerable, który znajduje się w przestrzeni nazw System.Collections. Klasy lub struktury, które obsługują to zachowanie, reklamują, że są w stanie udostępnić wywołującym zawarte elementy podrzędne (w tym przykładzie samo słowo kluczowe foreach). Oto definicja tego standardowego interfejsu .NET:

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

Jak widać, metoda GetEnumerator () zwraca odwołanie do jeszcze innego interfejsu o nazwie System.Collections.IEnumerator. Ten interfejs zapewnia infrastrukturę umożliwiającą rozmówcy przechodzenie przez wewnętrzne obiekty zawarte w kontenerze kompatybilnym z 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.
}

Jeśli chcesz zaktualizować typ garażu, aby obsługiwał te interfejsy, możesz przejść długą drogę i zaimplementować każdą metodę ręcznie. Chociaż z pewnością masz swobodę dostarczania niestandardowych wersji GetEnumerator (), MoveNext (), Current i Reset (), istnieje prostszy sposób. Ponieważ typ System.Array (jak również wiele innych klas kolekcji) już implementuje IEnumerable i IEnumerator, możesz po prostu delegować żądanie do System.Array w następujący sposób:

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

Po zaktualizowaniu typu garażu możesz bezpiecznie używać go w konstrukcji Cach foreach. Ponadto, biorąc pod uwagę, że metoda GetEnumerator () została zdefiniowana publicznie, użytkownik obiektu może również wchodzić w interakcje z typem 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);

Jeśli jednak wolisz ukryć funkcjonalność IEnumerable na poziomie obiektu, po prostu skorzystaj z jawnej implementacji interfejsu:

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

W ten sposób przypadkowy użytkownik obiektu nie znajdzie metody GarEnumerator () Garage, podczas gdy konstrukcja foreach uzyska interfejs w tle, gdy będzie to konieczne.

Na podstawie Pro C # 5.0 i .NET 4.5 Framework

Powiedział Roohullah Allem
źródło
1
Fantastyczna odpowiedź, dziękuję! Mam jednak jedno pytanie. W pierwszym przykładzie zapętlasz tablicę za pomocą foreach, ale nie możesz tego zrobić w drugim przykładzie. Czy to dlatego, że tablica jest w klasie, czy dlatego, że zawiera obiekty?
Caleb Palmquist
Dziękuję za wspaniałe wyjaśnienie. My tylko pytanie dlaczego po prostu nie ma sposobu, w Garagektóry pobiera carArray? W ten sposób nie musisz się wdrażać GetEnumerator, ponieważ Arrayjuż to robi. Egforeach(Car c in carLot.getCars()) { ... }
Nick Rolando
63

Implementacja IEnumerable oznacza, że ​​twoja klasa zwraca obiekt IEnumerator:

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

Wdrożenie IEnumerator oznacza, że ​​klasa zwraca metody i właściwości dla iteracji:

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

    public bool MoveNext()...

    public object Current...
}

To i tak różnica.

rdzeń
źródło
1
Fajnie, to wyjaśnia (wraz z innymi postami), jak przekonwertować klasę na klasę, w której można używać „foreach”, aby iterować po jej treści.
Contango,
54

Objaśnienie za pomocą Analogii + przewodnik po kodzie

Najpierw wyjaśnienie bez kodu, a następnie dodam je później.

Powiedzmy, że prowadzisz linię lotniczą. I w każdym samolocie chcesz znać informacje o pasażerach lecących samolotem. Zasadniczo chcesz być w stanie „przemierzać” samolot. Innymi słowy, chcesz móc zacząć na przednim siedzeniu, a następnie skierować się do tyłu samolotu, prosząc pasażerów o informacje: kim oni są, skąd pochodzą itp. Samolot może to zrobić tylko , Jeśli to jest:

  1. policzalne i
  2. jeśli ma licznik.

Dlaczego te wymagania? Ponieważ tego wymaga interfejs.

Jeśli jest to przeciążenie informacją, wystarczy wiedzieć, że chcesz zadać każdemu pasażerowi w samolocie kilka pytań, zaczynając od pierwszego do ostatniej.

Co znaczy liczyć?

Jeśli linia lotnicza jest „policzalna”, oznacza to, że MUSI być obecny stewardesa w samolocie, którego jedynym zadaniem jest liczyć - a stewardesa MUSI liczyć w bardzo szczególny sposób:

  1. Licznik / stewardesa MUSI wystartować przed pierwszym pasażerem (przed wszystkimi, gdzie demonstrują bezpieczeństwo, jak założyć kamizelkę ratunkową itp.).
  2. On / ona (tzn. Stewardesa) MUSI „przejść dalej” w górę przejścia do pierwszego miejsca.
  3. Następnie rejestruje: (i) osobę siedzącą w fotelu oraz (ii) ich bieżącą lokalizację w przejściu.

Procedury liczenia

Kapitan linii lotniczej chce sporządzić raport na temat każdego pasażera w momencie, gdy zostaną zbadani lub policzeni. Tak więc po rozmowie z osobą na pierwszym miejscu stewardesa / licznik następnie zgłasza się do kapitana, a gdy przekazany jest raport, licznik pamięta swoją dokładną pozycję w przejściu i kontynuuje liczenie od miejsca, w którym wyszedł poza.

W ten sposób kapitan zawsze może uzyskać informacje o bieżącej osobie, która jest przesłuchiwana. W ten sposób, jeśli dowie się, że ta osoba lubi Manchester City, może udzielić pasażerowi preferencyjnego traktowania itp.

  • Licznik idzie dalej, aż dotrze do końca samolotu.

Połączmy to z IEnumerables

  • Wyliczenie to tylko zbiór pasażerów w samolocie. Prawo o lotnictwie cywilnym - są to w zasadzie zasady, których muszą przestrzegać wszystkie numery IE. Za każdym razem, gdy stewardesa podchodzi do kapitana z informacją pasażera, w zasadzie „oddajemy” pasażera kapitanowi. Kapitan może w zasadzie robić wszystko, co chce z pasażerem - z wyjątkiem zmiany rozmieszczenia pasażerów w samolocie. W takim przypadku są traktowani preferencyjnie, jeśli podążają za Manchester City (ugh!)

    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)

Podsumowanie

Innymi słowy, coś można policzyć, jeśli ma licznik . I licznik musi (w zasadzie): (i) zapamiętać swoje miejsce ( stan ), (ii) móc przejść dalej , (iii) i wiedzieć o obecnej osobie, z którą ma do czynienia.

Wyliczenie to tylko wymyślne słowo na „policzalny”. Innymi słowy, wyliczalny pozwala „wyliczyć” (tj. Policzyć).

BKSpurgeon
źródło
23

IEnumerable implementuje GetEnumerator. Po wywołaniu ta metoda zwróci IEnumerator, który implementuje MoveNext, Reset i Current.

Tak więc, gdy twoja klasa implementuje IEnumerable, mówisz, że możesz wywołać metodę (GetEnumerator) i otrzymać nowy obiekt (IEnumerator), którego możesz użyć w pętli, takiej jak foreach.

DavGarcia
źródło
18

Implementacja IEnumerable umożliwia uzyskanie IEnumeratora dla listy.

IEnumerator pozwala na sekwencyjny dostęp do stylu foreach dla elementów na liście przy użyciu słowa kluczowego fed.

Przed implementacją foreach (na przykład w Javie 1.4) sposobem na iterację listy było pobranie enumeratora z listy, a następnie poproszenie go o pozycję „następną” na liście, o ile wartość zwracana jest jako następna pozycja nie jest pusta. Foreach robi to po prostu niejawnie jako funkcję języka, w taki sam sposób, w jaki lock () implementuje klasę Monitor za scenami.

Spodziewam się, że foreach działa na listach, ponieważ implementują IEnumerable.

Neil Barnwell
źródło
jeśli jest to lista, dlaczego nie mogę po prostu użyć foreach?
prodev42
Kaczka .Net wpisuje instrukcję foreach, ale IEnumerable / IEnumerable <T> to właściwy sposób stwierdzenia, że ​​twoja klasa może być wyliczona.
user7116
15
  • Obiekt implementujący IEnumerable umożliwia innym odwiedzanie każdego z jego elementów (przez moduł wyliczający) .
  • Obiekt implementujący IEnumerator wykonuje iterację. Pętla nad wymiennym obiektem.

Pomyśl o obiektach policzalnych jak o listach, stosach, drzewach.

Lieven Keersmaekers
źródło
12

IEnumerable i IEnumerator (i ich ogólne odpowiedniki IEnumerable <T> i IEnumerator <T>) są podstawowymi interfejsami implementacji iteratora w kolekcjach Libray klasy .Net Framework .

IEnumerable to najczęstszy interfejs, który można zobaczyć w większości kodu. Umożliwia pętlę foreach, generatory (myśl wydajność ), a ze względu na mały interfejs służy do tworzenia ścisłych abstrakcji. IEnumerable zależy od IEnumerator .

Z drugiej strony IEnumerator zapewnia interfejs iteracji nieco niższego poziomu. Jest to określane jako jawny iterator, który daje programiście większą kontrolę nad cyklem iteracji.

IEnumerable

IEnumerable to standardowy interfejs, który umożliwia iterację po kolekcjach, które go obsługują (w rzeczywistości wszystkie typy kolekcji, o których dziś myślę, implementują IEnumerable ). Obsługa kompilatora umożliwia takie funkcje językowe jak foreach. Ogólnie rzecz biorąc, umożliwia to niejawną implementację iteratora .

foreach Loop

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

Myślę, że foreachpętla jest jednym z głównych powodów używania interfejsów IEnumerable . foreachma bardzo zwięzłą składnię i jest bardzo łatwy do zrozumienia w porównaniu do klasycznego stylu C dla pętli, w których musisz sprawdzić różne zmienne, aby zobaczyć, co robi.

wydajność Słowo kluczowe

Prawdopodobnie mniej znaną cechą jest to, że IEnumerable włącza również generatory w języku C # za pomocą instrukcji yield returni yield break.

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

Abstrakcje

Innym częstym scenariuszem w praktyce jest użycie IEnumerable w celu zapewnienia minimalistycznych abstrakcji. Ponieważ jest to niewielki interfejs i tylko do odczytu, zachęca się do ujawnienia swoich kolekcji jako IEnumerable (zamiast na przykład List ). W ten sposób możesz swobodnie zmieniać swoją implementację bez łamania kodu klienta ( na przykład zmień List na LinkedList ).

Gotcha

Jednym z zachowań, o których należy pamiętać, jest to, że w implementacjach przesyłania strumieniowego (np. Pobieranie danych wiersz po rzędzie z bazy danych, zamiast ładowania wszystkich wyników do pamięci w pierwszej kolejności) nie można iterować kolekcji więcej niż jeden raz. Jest to sprzeczne z kolekcjami w pamięci, takimi jak Lista , w których można iterować wiele razy bez problemów. Na przykład ReSharper ma kontrolę kodu pod kątem możliwego wielokrotnego wyliczenia IEnumerable .

IEnumerator

Z drugiej strony IEnumerator to interfejs, który sprawia, że IEnumerble-foreach-magic działa. Ściśle mówiąc, umożliwia jawne iteratory.

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

Z mojego doświadczenia wynika, że IEnumerator jest rzadko używany w typowych scenariuszach ze względu na bardziej szczegółową składnię i nieco mylącą semantykę (przynajmniej dla mnie; np. MoveNext () również zwraca wartość, której nazwa wcale nie sugeruje).

Przypadek użycia dla IEnumerator

Korzystałem tylko z bibliotek IEnumerator, w szczególności (nieco niższego poziomu) bibliotek i frameworków, w których zapewniałem interfejsy IEnumerable . Jednym z przykładów jest biblioteka przetwarzania strumienia danych, która zapewniała szereg obiektów w foreachpętli, mimo że za kulisami dane były zbierane przy użyciu różnych strumieni plików i serializacji.

Kod klienta

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

Biblioteka

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;   
    }
}
ziya
źródło
9

Implementacja IEnumerablezasadniczo oznacza, że ​​obiekt może być iterowany. Nie musi to oznaczać, że jest to tablica, ponieważ istnieją pewne listy, których nie można indeksować, ale można je wyliczyć.

IEnumeratorjest rzeczywistym obiektem używanym do wykonywania iteracji. Kontroluje przechodzenie od jednego obiektu do drugiego na liście.

Przez większość czasu IEnumerable& IEnumeratorsą używane w sposób przezroczysty jako część foreachpętli.

Dan Herbert
źródło
6

Różnice między IEnumerable i IEnumerator:

  • IEnumerable używa IEnumerator wewnętrznie.
  • IEnumerable nie wie, który element / obiekt wykonuje.
  • Ilekroć przekazujemy IEnumerator do innej funkcji, zna ona bieżącą pozycję elementu / obiektu.
  • Ilekroć przekazujemy kolekcję IEnumerable do innej funkcji, nie zna ona aktualnej pozycji elementu / obiektu (nie wie, który element wykonuje)

    IEnumerable ma jedną metodę GetEnumerator ()

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

IEnumerator ma jedną właściwość o nazwie Bieżąca i dwie metody: Reset () i MoveNext () (co jest przydatne do poznania aktualnej pozycji elementu na liście).

public interface IEnumerator
{
     object Current { get; }
     bool MoveNext();
     void Reset();
}
Ajay
źródło
5

IEnumerable to pole zawierające Ienumerator. IEnumerable to podstawowy interfejs dla wszystkich kolekcji. Pętla foreach może działać, jeśli kolekcja implementuje IEnumerable. W poniższym kodzie wyjaśniono etap posiadania własnego modułu wyliczającego. Najpierw określmy naszą klasę, której kolekcję będziemy tworzyć.

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

Teraz zdefiniujemy klasę, która będzie działać jako kolekcja dla naszego klienta klasy. Zauważ, że implementuje interfejs IEnumerable. Dlatego musimy wdrożyć metodę GetEnumerator. Zwróci to nasz niestandardowy moduł wyliczający.

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

Teraz stworzymy własny niestandardowy moduł wyliczający w następujący sposób. Musimy więc zaimplementować metodę 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.
        }
    }

Teraz możemy użyć pętli foreach w naszej kolekcji, jak poniżej;

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

        }
    }
Amir
źródło
4

Zrozumienie wzoru Iteratora będzie dla ciebie pomocne. Polecam przeczytać to samo.

Wzór iteratora

Na wysokim poziomie wzorzec iteratora może być używany do zapewnienia standardowego sposobu iteracji przez kolekcje dowolnego typu. Mamy 3 uczestników wzorca iteratora, faktycznej kolekcji (klienta), agregatora i iteratora. Agregat to klasa interfejsu / abstrakcyjna, która ma metodę zwracającą iterator. Iterator to klasa interfejsu / abstrakcyjna, która ma metody pozwalające na iterację w kolekcji.

Aby zaimplementować wzorzec, musimy najpierw zaimplementować iterator, aby uzyskać konkretny element, który może iterować po danej kolekcji (kliencie). Następnie kolekcja (klient) implementuje agregator, aby zwrócić instancję powyższego iteratora.

Oto schemat UML Wzór iteratora

Więc w zasadzie w c #, IEnumerable jest abstrakcyjnym agregatem, a IEnumerator jest abstrakcyjnym Iteratorem. IEnumerable ma jedną metodę GetEnumerator, która jest odpowiedzialna za utworzenie instancji IEnumerator żądanego typu. Kolekcje takie jak Listy implementują IEnumerable.

Przykład. Załóżmy, że mamy metodę, getPermutations(inputString)która zwraca wszystkie permutacje łańcucha i że metoda zwraca instancjęIEnumerable<string>

Aby policzyć liczbę permutacji, możemy zrobić coś takiego jak poniżej.

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

Kompilator c # mniej więcej konwertuje powyższe na

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

Jeśli masz jakieś pytania, nie wahaj się zadać.

lalatnayak
źródło
2

Niewielki wkład.

Ponieważ wiele z nich wyjaśnia „kiedy używać” i „używać z foreach”. Pomyślałem o dodaniu tutaj innej różnicy stanów, o co pytano w pytaniu o różnicę między IEnumerable IEnumerator.

Utworzyłem poniższy przykład kodu na podstawie poniższych wątków dyskusji.

IEnumerable, IEnumerator vs foreach, kiedy używać Jaka jest różnica między IEnumerator a IEnumerable?

Moduł wyliczający zachowuje stan (pozycję iteracji) między wywołaniami funkcji, podczas gdy iteracje z drugiej strony nie.

Oto przetestowany przykład z komentarzami do zrozumienia.

Eksperci, dodaj mnie / popraw mnie.

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());
    }
}
Sai
źródło
2

Zauważyłem te różnice:

Odp .: Iterujemy listę w inny sposób, foreach może być użyty dla IEnumerable, a while dla IEnumerator.

B. IEnumerator może zapamiętać bieżący indeks, kiedy przechodzimy z jednej metody do drugiej (zaczyna działać z bieżącym indeksem), ale IEnumerable nie może zapamiętać indeksu i resetuje indeks do początku. Więcej w tym wideo https://www.youtube.com/watch?v=jd3yUjGc9M0

RotatingWheel
źródło
1

IEnumerablei IEnumeratoroba są interfejsami w języku C #.

IEnumerableto interfejs definiujący pojedynczą metodę GetEnumerator()zwracającą IEnumeratorinterfejs.

Działa to w przypadku dostępu tylko do odczytu do kolekcji, której implementacje IEnumerablemogą być używane z foreachinstrukcją.

IEnumeratorma dwie metody MoveNexti Reset. Ma również właściwość o nazwie Current.

Poniżej przedstawiono implementację IEnumerable i IEnumerator.

Jihed Jaoidi
źródło
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
  */
Md Shahriar
źródło