Qualcuno può spiegarmi IEnumerable e IEnumerator? [chiuso]


272

Qualcuno può spiegarmi IEnumerable e IEnumerator?

per esempio, quando usarlo su foreach? qual è la differenza tra IEnumerable e IEnumerator? Perché dobbiamo usarlo?


55
Questo è il peggior mix di denominazione da Java e JavaScript
Matthew Lock,

@MatthewLock puoi spiegare cosa intendi?
David Klempfner,

Risposte:


275

per esempio, quando usarlo su foreach?

Non usi IEnumerable"over" foreach. L'implementazione IEnumerablerende foreach possibile l' utilizzo .

Quando scrivi codice come:

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

è funzionalmente equivalente alla scrittura:

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

Per "funzionalmente equivalente", intendo che in realtà è quello in cui il compilatore trasforma il codice. Non è possibile utilizzare foreachilbaz in questo esempio a meno che baz implementa IEnumerable.

IEnumerable significa che baz implementa il metodo

IEnumerator GetEnumerator()

Il IEnumerator oggetto restituito da questo metodo deve implementare i metodi

bool MoveNext()

e

Object Current()

Il primo metodo avanza all'oggetto successivo in IEnumerable che ha creato l'enumeratore, restituendo falsese è stato eseguito e il secondo restituisce l'oggetto corrente.

Qualunque cosa in .Net che puoi iterare sugli strumenti IEnumerable. Se stai costruendo la tua classe e non eredita già da una classe che implementa IEnumerable, puoi rendere la tua classe utilizzabile nelle foreachistruzioni implementando IEnumerable(e creando una classe enumeratrice che il suo nuovo GetEnumeratormetodo restituirà).


15
Penso che quando il poster originale dicesse "over foreach", intendeva "quando dovrei chiamare GetEnumerator () / MoveNext () in modo esplicito invece di utilizzare un ciclo foreach." Per quello che vale.
mqp,

6
Ah, penso che tu abbia ragione. Bene, la risposta è implicita nel mio post: non importa, dato che è comunque quello che fa il compilatore. Quindi fai ciò che è più semplice, cioè usa foreach.
Robert Rossney,

12
Questa risposta non racconta l'intera storia. Gli oggetti enumerabili non devono implementare IEnumerable; devono solo avere un metodo GetEnumerator che restituisce un'istanza di un tipo che a sua volta ha un bool MoveNext()metodo e una Currentproprietà di qualsiasi tipo. Questo approccio "tipizzazione anatra" è stato implementato in C # 1.0 come un modo per evitare il pugilato durante l'enumerazione di raccolte di tipi di valore.
phoog

3
Con l'esaltazione dall'alto e lavorando attraverso Workthrough: Implementando IEnumerable di (T) hai fonti di Greate.
Ruedi,

4
È un po 'come un C ++ iterator?
Matt G,

162

Le interfacce IEnumerable e IEnumerator

Per iniziare a esaminare il processo di implementazione delle interfacce .NET esistenti, esaminiamo innanzitutto il ruolo di IEnumerable e IEnumerator. Ricorda che C # supporta una parola chiave denominata foreach che ti consente di scorrere i contenuti di qualsiasi tipo di array:

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

Sebbene possa sembrare che solo i tipi di array possano utilizzare questo costrutto, la verità è che qualsiasi tipo che supporti un metodo chiamato GetEnumerator () può essere valutato dal costrutto foreach. Per illustrare, seguimi!

Supponiamo di avere una classe Garage:

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

Idealmente, sarebbe conveniente scorrere le voci secondarie dell'oggetto Garage usando il costrutto foreach, proprio come una matrice di valori di dati:

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

Purtroppo, il compilatore ti informa che la classe Garage non implementa un metodo chiamato GetEnumerator (). Questo metodo è formalizzato dall'interfaccia IEnumerable, che si trova in agguato nello spazio dei nomi System.Collections. Le classi o le strutture che supportano questo comportamento pubblicizzano che sono in grado di esporre al chiamante elementi secondari contenuti (in questo esempio, la parola chiave foreach stessa). Ecco la definizione di questa interfaccia .NET standard:

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

Come puoi vedere, il metodo GetEnumerator () restituisce un riferimento a un'altra interfaccia denominata System.Collections.IEnumerator. Questa interfaccia fornisce l'infrastruttura per consentire al chiamante di attraversare gli oggetti interni contenuti nel contenitore compatibile con 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.
}

Se desideri aggiornare il tipo di Garage per supportare queste interfacce, potresti prendere la strada lunga e implementare manualmente ogni metodo. Sebbene tu sia certamente libero di fornire versioni personalizzate di GetEnumerator (), MoveNext (), Current e Reset (), esiste un modo più semplice. Poiché il tipo System.Array (così come molte altre classi di raccolta) implementa già IEnumerable e IEnumerator, puoi semplicemente delegare la richiesta a System.Array come segue:

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

Dopo aver aggiornato il tipo di Garage, puoi tranquillamente utilizzare il tipo all'interno del costrutto foreach di C #. Inoltre, dato che il metodo GetEnumerator () è stato definito pubblicamente, l'utente oggetto potrebbe anche interagire con il tipo 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);

Tuttavia, se si preferisce nascondere la funzionalità di IEnumerable a livello di oggetto, utilizzare semplicemente l'implementazione esplicita dell'interfaccia:

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

In questo modo, l'utente dell'oggetto casuale non troverà il metodo GetEnumerator () di Garage, mentre il costrutto foreach otterrà l'interfaccia in background quando necessario.

Adattato da Pro C # 5.0 e .NET 4.5 Framework


1
Risposta fantastica, grazie! Ho una domanda però. Nel primo esempio esegui il ciclo sull'array usando foreach ma non puoi farlo nel secondo esempio. È perché l'array è in una classe o perché contiene oggetti?
Caleb Palmquist,

Grazie per l'ottima spiegazione. La mia unica domanda è: perché non avere solo un metodo per Garageottenere carArray? In questo modo non è necessario implementare GetEnumeratorte stesso, poiché Arraygià lo fa. Ad esempioforeach(Car c in carLot.getCars()) { ... }
Nick Rolando, il

63

L'implementazione di IEnumerable significa che la tua classe restituisce un oggetto IEnumerator:

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

L'implementazione di IEnumerator significa che la tua classe restituisce i metodi e le proprietà per l'iterazione:

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

    public bool MoveNext()...

    public object Current...
}

Questa è la differenza comunque.


1
Bene, questo spiega (insieme agli altri post) come convertire la tua classe in una su cui puoi usare "foreach" per scorrere sui suoi contenuti.
Contango,

54

Spiegazione tramite Analogia + Codice Soluzione

Prima una spiegazione senza codice, poi la aggiungerò più avanti.

Diciamo che stai gestendo una compagnia aerea. E in ogni aereo vuoi sapere informazioni sui passeggeri che volano nell'aereo. Fondamentalmente vuoi essere in grado di "attraversare" l'aereo. In altre parole, vuoi essere in grado di iniziare dal sedile anteriore e poi avanzare verso la parte posteriore dell'aereo, chiedendo ai passeggeri alcune informazioni: chi sono, da dove vengono, ecc. Un aereo può fare solo questo , se è:

  1. numerabile, e
  2. se ha un contatore.

Perché questi requisiti? Perché è quello che richiede l'interfaccia.

Se questo è un sovraccarico di informazioni, tutto ciò che devi sapere è che vuoi essere in grado di porre alcune domande a ciascun passeggero dell'aereo, partendo dal primo e arrivando all'ultimo.

Cosa significa numerabile?

Se una compagnia aerea è "numerabile", ciò significa che DEVE essere presente un assistente di volo sull'aereo, il cui unico compito è contare - e che tale assistente di volo DEVE contare in un modo molto specifico:

  1. Il contatore / assistente di volo DEVE iniziare prima del primo passeggero (davanti a tutti quelli che dimostrano la sicurezza, come indossare il giubbotto di salvataggio, ecc.).
  2. Lui / lei (cioè l'assistente di volo) DEVE "spostarsi dopo" lungo il corridoio fino al primo posto.
  3. Dovrà quindi registrare: (i) chi è la persona seduta, e (ii) la posizione corrente nel corridoio.

Procedure di conteggio

Il capitano della compagnia aerea desidera un rapporto su ogni passeggero man mano che viene esaminato o conteggiato. Quindi, dopo aver parlato con la persona al primo posto, l'assistente di volo / segnalino quindi riporta al capitano e, quando viene dato il rapporto, il contatore ricorda la sua posizione esatta nella navata e continua a contare proprio dove ha lasciato off.

In questo modo il capitano è sempre in grado di avere informazioni sull'attuale persona indagata. In questo modo, se scopre che a questa persona piace Manchester City, allora può dare a quel passeggero un trattamento preferenziale ecc.

  • Il contatore continua fino a quando non raggiunge la fine dell'aereo.

Legiamolo a IEnumerables

  • Un enumerabile è solo una raccolta di passeggeri su un aereo. La legge sull'aviazione civile - queste sono fondamentalmente le regole che tutti gli Enumerabili devono seguire. Ogni volta che l'assistente della compagnia aerea si rivolge al capitano con le informazioni del passeger, in pratica 'cediamo' il passeggero al capitano. Il capitano può praticamente fare quello che vuole con il passeggero, tranne riorganizzare i passeggeri sull'aereo. In questo caso, ricevono un trattamento preferenziale se seguono 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)

Sommario

In altre parole, qualcosa è numerabile se ha un contatore . E il contatore deve (in sostanza): (i) ricordare il suo posto ( stato ), (ii) essere in grado di spostarsi dopo , (iii) e conoscere la persona attuale con cui ha a che fare.

Enumerabile è solo una parola elaborata per "numerabile". In altre parole, un enumerabile consente di "enumerare" (ovvero contare).


23

IEnumerable implementa GetEnumerator. Quando viene chiamato, quel metodo restituirà un IEnumerator che implementa MoveNext, Reset e Current.

Pertanto, quando la tua classe implementa IEnumerable, stai dicendo che puoi chiamare un metodo (GetEnumerator) e ottenere un nuovo oggetto restituito (un IEnumerator) che puoi usare in un ciclo come foreach.


18

L'implementazione di IEnumerable consente di ottenere un IEnumerator per un elenco.

IEnumerator consente l'accesso sequenziale in stile foreach agli elementi nell'elenco, utilizzando la parola chiave yield.

Prima di procedere all'implementazione (ad esempio in Java 1.4), il modo per iterare un elenco era ottenere un enumeratore dall'elenco, quindi chiedergli l'elemento "successivo" nell'elenco, purché il valore restituito come successivo l'articolo non è nullo. Foreach lo fa semplicemente implicitamente come funzionalità del linguaggio, allo stesso modo in cui lock () implementa la classe Monitor dietro le quinte.

Mi aspetto che tutti gli elenchi funzionino perché implementano IEnumerable.


se è per un elenco, perché non posso semplicemente usare foreach?
prodev42,

.Net duck digita l'istruzione foreach, ma IEnumerable / IEnumerable <T> è il modo appropriato per dire che la tua classe può essere enumerata.
user7116,

15
  • Un oggetto che implementa IEnumerable consente ad altri di visitare ciascuno dei suoi elementi (da un enumeratore) .
  • Un oggetto che implementa IEnumerator è l'esecuzione dell'iterazione. Si ripete su un oggetto enumerabile.

Pensa agli oggetti enumerabili come a liste, pile, alberi.


12

IEnumerable e IEnumerator (e le loro controparti generiche IEnumerable <T> e IEnumerator <T>) sono interfacce di base delle implementazioni di iteratori nelle raccolte Libray di classe .Net Framework .

IEnumerable è l'interfaccia più comune che vedresti nella maggior parte del codice là fuori. Abilita il ciclo foreach, i generatori (pensa alla resa ) e grazie alla sua piccola interfaccia, viene utilizzato per creare astrazioni strette. IEnumerable dipende da IEnumerator .

IEnumerator , d'altra parte, fornisce un'interfaccia di iterazione di livello leggermente inferiore. Viene chiamato iteratore esplicito che offre al programmatore un maggiore controllo sul ciclo di iterazione.

IEnumerable

IEnumerable è un'interfaccia standard che consente l'iterazione su raccolte che la supportano (in effetti, tutti i tipi di raccolta che posso pensare oggi implementano IEnumerable ). Il supporto del compilatore consente funzionalità linguistiche come foreach. In termini generali, consente questa implementazione iteratrice implicita .

foreach Loop

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

Penso che il foreachloop sia uno dei motivi principali per l'utilizzo delle interfacce IEnumerable . foreachha una sintassi molto sintetica e molto facile da capire rispetto al classico stile C per i loop in cui è necessario controllare le varie variabili per vedere cosa stava facendo.

cedere parola chiave

Probabilmente una caratteristica meno nota è che IEnumerable abilita anche generatori in C # con l'uso di yield returne yield breakistruzioni.

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

astrazioni

Un altro scenario comune in pratica è l'utilizzo di IEnumerable per fornire astrazioni minimaliste. Poiché si tratta di un'interfaccia minuscola e di sola lettura, si consiglia di esporre le raccolte come IEnumerable (anziché come Elenco, ad esempio). In questo modo sei libero di modificare l'implementazione senza rompere il codice del tuo client (ad esempio, cambiare l'elenco in un elenco collegato ).

Gotcha

Un comportamento da tenere presente è che nelle implementazioni di streaming (ad esempio il recupero di dati riga per riga da un database, anziché caricare prima tutti i risultati in memoria) non è possibile scorrere più volte la raccolta. Ciò è in contrasto con le raccolte in memoria come Elenco , in cui è possibile ripetere più volte senza problemi. ReSharper, ad esempio, ha un'ispezione del codice per la possibile enumerazione multipla di IEnumerable .

IEnumerator

IEnumerator, d'altra parte, è l'interfaccia dietro le quinte che fa funzionare IEnumerble-foreach-magic . A rigor di termini, consente iteratori espliciti.

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

Nella mia esperienza, IEnumerator è usato raramente in scenari comuni a causa della sua sintassi più dettagliata e della semantica leggermente confusa (almeno per me; ad esempio MoveNext () restituisce anche un valore, che il nome non suggerisce affatto).

Caso d'uso per IEnumerator

Ho usato IEnumerator solo in librerie e framework (di livello leggermente inferiore) in cui fornivo interfacce IEnumerable . Un esempio è una libreria di elaborazione del flusso di dati che fornisce serie di oggetti in un foreachciclo anche se i dati dietro le quinte sono stati raccolti usando vari flussi di file e serializzazioni.

Codice cliente

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

Biblioteca

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

L'implementazione IEnumerablesignifica essenzialmente che l'oggetto può essere ripetuto. Ciò non significa necessariamente che sia un array in quanto vi sono alcuni elenchi che non possono essere indicizzati ma è possibile elencarli.

IEnumeratorè l'oggetto effettivo utilizzato per eseguire le iterazioni. Controlla lo spostamento da un oggetto a quello successivo nell'elenco.

Il più delle volte, IEnumerablee IEnumeratorvengono utilizzati in modo trasparente come parte di un foreachciclo.


6

Differenze tra IEnumerable e IEnumerator:

  • IEnumerable utilizza IEnumerator internamente.
  • IEnumerable non sa quale oggetto / oggetto sta eseguendo.
  • Ogni volta che passiamo IEnumerator a un'altra funzione, conosce la posizione corrente dell'oggetto / oggetto.
  • Ogni volta che passiamo una raccolta IEnumerable a un'altra funzione, non conosce la posizione corrente di item / object (non sa quale articolo sta eseguendo)

    IEnumerable ha un metodo GetEnumerator ()

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

IEnumerator ha una proprietà chiamata Current e due metodi, Reset () e MoveNext () (che è utile per conoscere la posizione corrente di un elemento in un elenco).

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

5

IEnumerable è una casella che contiene Ienumerator. IEnumerable è l'interfaccia di base per tutte le raccolte. foreach loop può funzionare se la raccolta implementa IEnumerable. Nel codice seguente viene spiegato il passaggio per avere il nostro enumeratore. Definiamo prima la nostra classe di cui faremo la raccolta.

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

Ora definiremo la Classe che fungerà da raccolta per il nostro Cliente di classe. Si noti che sta implementando l'interfaccia IEnumerable. In modo che dobbiamo implementare il metodo GetEnumerator. Questo restituirà il nostro enumeratore personalizzato.

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

Ora creeremo il nostro enumeratore personalizzato come segue. Quindi, dobbiamo implementare il metodo 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.
        }
    }

Ora possiamo usare foreach loop sulla nostra collezione come di seguito;

    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

Una comprensione del modello Iterator ti sarà utile. Consiglio di leggere lo stesso.

Modello Iteratore

Ad un livello elevato, il modello iteratore può essere utilizzato per fornire un modo standard di iterare attraverso raccolte di qualsiasi tipo. Abbiamo 3 partecipanti nel modello iteratore, la raccolta effettiva (client), l'aggregatore e l'iteratore. L'aggregato è un'interfaccia / classe astratta che ha un metodo che restituisce un iteratore. Iterator è un'interfaccia / classe astratta che ha metodi che ci consentono di scorrere attraverso una raccolta.

Per implementare il modello, dobbiamo prima implementare un iteratore per produrre un concreto che possa iterare sulla raccolta (client) interessata. Quindi la raccolta (client) implementa l'aggregatore per restituire un'istanza dell'iteratore sopra.

Ecco il diagramma UML Modello Iteratore

Quindi sostanzialmente in c #, IEnumerable è l'aggregato astratto e IEnumerator è l'Iteratore astratto. IEnumerable ha un unico metodo GetEnumerator che è responsabile della creazione di un'istanza di IEnumerator del tipo desiderato. Collezioni come Elenchi implementano IEnumerable.

Esempio. Supponiamo che abbiamo un metodo getPermutations(inputString)che restituisce tutte le permutazioni di una stringa e che il metodo restituisce un'istanza diIEnumerable<string>

Per contare il numero di permutazioni potremmo fare qualcosa di simile al seguito.

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

Il compilatore c # più o meno converte quanto sopra in

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

Se avete domande, non esitate a chiedere.


2

Un contributo minore.

Come molti di loro spiegano "quando usare" e "usare con foreach". Ho pensato di aggiungere qui un'altra differenza di stati , come richiesto nella domanda sulla differenza tra IEnumerable e IEnumerator.

Ho creato l'esempio di codice riportato di seguito in base ai thread di discussione seguenti.

IEnumerable, IEnumerator vs foreach, quando usare qual è la differenza tra IEnumerator e IEnumerable?

L'enumeratore conserva lo stato (posizione di iterazione) tra le chiamate di funzione mentre le iterazioni invece Enumerable no.

Ecco l'esempio testato con commenti per capire.

Esperti per favore aggiungimi / correggimi.

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

Ho notato queste differenze:

R. Ripetiamo l'elenco in modo diverso, foreach può essere utilizzato per IEnumerable e while loop per IEnumerator.

B. IEnumerator può ricordare l'indice corrente quando passiamo da un metodo all'altro (inizia a lavorare con l'indice corrente) ma IEnumerable non riesce a ricordare l'indice e reimposta l'indice all'inizio. Altro in questo video https://www.youtube.com/watch?v=jd3yUjGc9M0


1

IEnumerableed IEnumeratorentrambi sono interfacce in C #.

IEnumerable è un'interfaccia che definisce un singolo metodo GetEnumerator() che restituisce unIEnumerator un'interfaccia.

Funziona per l'accesso in sola lettura a una raccolta che lo implementa IEnumerable può essere utilizzata con foreachun'istruzione.

IEnumeratorha due metodi MoveNexteReset . Ha anche una proprietà chiamata Current.

Quanto segue mostra l'implementazione di IEnumerable e 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
  */
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.