que c# ienumerable ienumerator

que - set ienumerable c#



¿Alguien puede explicarme IEnumerable y IEnumerator? (14)

¿Alguien puede explicarme IEnumerable y IEnumerator?

por ejemplo, ¿cuándo usarlo sobre foreach? ¿Cuál es la diferencia entre IEnumerable e IEnumerator? ¿Por qué necesitamos usarlo?


por ejemplo, ¿cuándo usarlo sobre foreach?

No usas IEnumerable "over" foreach . Implementar IEnumerable hace posible el uso de foreach .

Cuando escribes código como:

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

Es funcionalmente equivalente a escribir:

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

Por "funcionalmente equivalente", quiero decir que en realidad eso es lo que el compilador convierte en código. No puede usar foreach en baz en este ejemplo a menos que baz implemente IEnumerable .

IEnumerable significa que baz implementa el método.

IEnumerator GetEnumerator()

El objeto IEnumerator que devuelve este método debe implementar los métodos

bool MoveNext()

y

Object Current()

El primer método avanza al siguiente objeto en el objeto IEnumerable que creó el enumerador, devolviendo false si se hace, y el segundo devuelve el objeto actual.

Cualquier cosa en .Net que pueda iterar sobre los implementos IEnumerable . Si está creando su propia clase y no se hereda de una clase que implementa IEnumerable , puede hacer que su clase sea utilizable en las declaraciones foreach implementando IEnumerable (y creando una clase de enumerador que devolverá su nuevo método GetEnumerator ) .


Explicación vía analogía + tutorial de código

Primero una explicación sin código, luego la añadiré más tarde.

Digamos que está ejecutando una compañía aérea. Y en cada avión quieres saber información sobre los pasajeros que vuelan en el avión. Básicamente quieres poder "atravesar" el plano. En otras palabras, desea poder comenzar en el asiento delantero y luego dirigirse hacia la parte trasera del avión, preguntando a los pasajeros información: quiénes son, de dónde son, etc. Un avión solo puede hacer esto. , si esto es:

  1. contable, y
  2. Si tiene un contador.

¿Por qué estos requisitos? Porque eso es lo que requiere la interfaz.

Si se trata de una sobrecarga de información, todo lo que necesita saber es que desea poder hacerle algunas preguntas a cada pasajero en el avión, comenzando desde el primero hasta llegar al último.

¿Qué significa contable?

Si una aerolínea es "contable", esto significa que DEBE haber un asistente de vuelo presente en el avión, quién tiene el único trabajo de contar, y este asistente de vuelo DEBE contar de una manera muy específica:

  1. El contador / azafata DEBE comenzar antes del primer pasajero (en la parte delantera de todas las personas en las que demuestra seguridad, cómo ponerse el chaleco salvavidas, etc.).
  2. Él / ella (es decir, el asistente de vuelo) DEBE "moverse a continuación" por el pasillo hasta el primer asiento.
  3. Luego debe registrar: (i) quién es la persona en el asiento, y (ii) su ubicación actual en el pasillo.

Procedimientos de conteo

El capitán de la aerolínea desea un informe sobre cada pasajero a medida que se investiga o se cuenta. Entonces, después de hablar con la persona en el primer asiento, el auxiliar de vuelo / mostrador luego informa al capitán, y cuando se da el informe, el contador recuerda su posición exacta en el pasillo y continúa contando justo donde lo dejó. apagado.

De esta manera, el capitán siempre puede tener información sobre la persona actual que está siendo investigada. De esa manera, si descubre que a este individuo le gusta el Manchester City, entonces puede darle a ese pasajero un trato preferencial, etc.

  • El contador sigue avanzando hasta que llega al final del avión.

Vamos a atar esto con los IEnumerables

  • Un enumerable es solo una colección de pasajeros en un avión. La Ley de aviación civil: estas son básicamente las reglas que deben cumplir todos los IEnumerables. Cada vez que el operador de la aerolínea acude al capitán con la información del pasajero, básicamente estamos "cediendo" el pasajero al capitán. El capitán básicamente puede hacer lo que quiera con el pasajero, excepto reorganizar a los pasajeros en el avión. En este caso, se les da un tratamiento preferencial si siguen la ciudad de Manchester (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)

Resumen

En otras palabras, algo es contable si tiene un contador . Y el contador debe (básicamente): (i) recordar su lugar ( estado ), (ii) ser capaz de moverse a continuación , (iii) y saber sobre la persona actual con la que está tratando.

Enumerable es sólo una palabra elegante para "contable". En otras palabras, un enumerable le permite "enumerar" (es decir, contar).


He notado estas diferencias:

A. Recorremos la lista de manera diferente, foreach puede usarse para IEnumerable y while loop para IEnumerator.

B. IEnumerator puede recordar el índice actual cuando pasamos de un método a otro (comienza a trabajar con el índice actual), pero IEnumerable no puede recordar el índice y restablecer el índice al principio. Más en este video https://www.youtube.com/watch?v=jd3yUjGc9M0


IEnumerable e IEnumerator (y sus contrapartes genéricas IEnumerable <T> y IEnumerator <T>) son interfaces base de implementaciones de iterator en colecciones de .Net Framework Class Libray .

IEnumerable es la interfaz más común que vería en la mayoría del código que existe. Permite el bucle foreach, los generadores (piense el rendimiento ) y, debido a su pequeña interfaz, se utiliza para crear abstracciones ajustadas. IEnumerable depende de IEnumerator .

IEnumerator , por otro lado, proporciona una interfaz de iteración de nivel ligeramente inferior. Se le conoce como el iterador explícito que le da al programador más control sobre el ciclo de iteración.

Enumerable

IEnumerable es una interfaz estándar que permite iterar sobre colecciones que lo admiten (de hecho, todos los tipos de colección que se me ocurren hoy implementan IEnumerable ). El soporte del compilador permite características de lenguaje como foreach . En términos generales, habilita esta implementación de iterador implícito .

foreach Loop

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

Creo que el bucle foreach es una de las razones principales para usar interfaces IEnumerable . foreach tiene una sintaxis muy concisa y muy fácil de entender en comparación con el estilo clásico de C para los bucles en los que necesita verificar las diversas variables para ver qué estaba haciendo.

palabra clave de rendimiento

Probablemente una característica menos conocida es que IEnumerable también habilita generadores en C # con el uso de yield return de yield return y yield break .

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

Abstracciones

Otro escenario común en la práctica es usar IEnumerable para proporcionar abstracciones minimalistas. Debido a que es una interfaz minúscula y de solo lectura, se recomienda que exponga sus colecciones como IEnumerable (en lugar de Lista, por ejemplo). De esa manera, usted es libre de cambiar su implementación sin romper el código de su cliente (por ejemplo, cambie la Lista a una Lista Vinculada ).

Gotcha

Un comportamiento que debe tener en cuenta es que en las implementaciones de transmisión por secuencias (por ejemplo, la recuperación de datos fila por fila de una base de datos, en lugar de cargar todos los resultados en la memoria primero) no puede recorrer la colección más de una vez. Esto contrasta con las colecciones en memoria como Lista , donde puede iterar varias veces sin problemas. ReSharper, por ejemplo, tiene una inspección de código para una posible enumeración múltiple de IEnumerable .

IEnumerador

IEnumerator, por otro lado, es la interfaz detrás de escena que hace que IEnumerble-foreach-magic work. Hablando estrictamente, permite iteradores explícitos.

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

En mi experiencia, IEnumerator rara vez se usa en escenarios comunes debido a su sintaxis más detallada y una semántica ligeramente confusa (al menos para mí; por ejemplo, MoveNext () también devuelve un valor, que el nombre no sugiere en absoluto).

Caso de uso para IEnumerator

Solo utilicé IEnumerator en particular (nivel ligeramente inferior) las bibliotecas y los marcos en los que proporcionaba interfaces IEnumerable . Un ejemplo es una biblioteca de procesamiento de flujo de datos que proporcionó una serie de objetos en un bucle foreach aunque los datos detrás de la escena se recopilaron utilizando varios flujos de archivos y serializaciones.

Codigo del 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; } }


Implementar IEnumerable esencialmente significa que el objeto puede ser iterado. Esto no significa necesariamente que sea una matriz, ya que hay ciertas listas que no se pueden indexar, pero se pueden enumerar.

IEnumerator es el objeto real utilizado para realizar las iteraciones. Controla el movimiento de un objeto a otro en la lista.

La mayoría de las veces, IEnumerable e IEnumerator se usan de manera transparente como parte de un bucle foreach .


Implementar IEnumerable significa que su clase devuelve un objeto IEnumerator:

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

Implementar IEnumerator significa que su clase devuelve los métodos y propiedades para la iteración:

public class PeopleEnumerator : IEnumerator { public void Reset()... public bool MoveNext()... public object Current... }

Esa es la diferencia de todos modos.


La implementación de IEnumerable le permite obtener un IEnumerator para una lista.

IEnumerator permite el acceso secuencial de cada estilo a los elementos de la lista, utilizando la palabra clave de rendimiento.

Antes de la implementación de foreach (en Java 1.4, por ejemplo), la forma de iterar una lista era obtener un enumerador de la lista, luego pedirle el "siguiente" elemento de la lista, siempre y cuando el valor se devuelva como el siguiente El elemento no es nulo. Foreach simplemente hace eso implícitamente como una característica de lenguaje, de la misma manera que lock () implementa la clase Monitor detrás de la escena.

Espero que foreach trabaje en listas porque implementan IEnumerable.


Una comprensión del patrón de iterador será útil para usted. Recomiendo leer lo mismo.

Patrón de iterador

En un nivel alto, el patrón de iterador se puede utilizar para proporcionar una forma estándar de iteración a través de colecciones de cualquier tipo. Tenemos 3 participantes en el patrón de iterador, la colección real (cliente), el agregador y el iterador. El agregado es una interfaz / clase abstracta que tiene un método que devuelve un iterador. Iterator es una clase de interfaz / abstracta que tiene métodos que nos permiten iterar a través de una colección.

Para implementar el patrón, primero necesitamos implementar un iterador para producir un concreto que pueda iterar sobre la colección (cliente) correspondiente. Luego, la colección (cliente) implementa el agregador para devolver una instancia del iterador anterior.

Aquí está el diagrama UML

Así que básicamente en c #, IEnumerable es el agregado abstracto e IEnumerator es el iterador abstracto. IEnumerable tiene un solo método GetEnumerator que es responsable de crear una instancia de IEnumerator del tipo deseado. Colecciones como Listas implementan el IEnumerable.

Ejemplo. Supongamos que tenemos un método getPermutations(inputString) que devuelve todas las permutaciones de una cadena y que el método devuelve una instancia de IEnumerable<string>

Para contar el número de permutaciones, podríamos hacer algo como lo siguiente.

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

El compilador c # más o menos convierte lo anterior a

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

Si tiene alguna pregunta no dude en preguntar.


Una contribución menor.

Como muchos de ellos explican sobre "cuándo usar" y "usar con foreach". Pensé en agregar Otra diferencia de estados aquí como se solicita en la pregunta acerca de la diferencia entre IEnumerable y IEnumerator.

Creé el siguiente ejemplo de código basado en los hilos de discusión a continuación.

IEnumerable, IEnumerator vs foreach, cuándo usar cuál ¿Cuál es la diferencia entre IEnumerator y IEnumerable?

El enumerador conserva el estado (posición de iteración) entre las llamadas de función, mientras que las iteraciones, por otro lado, Enumerable no.

Aquí está el ejemplo probado con comentarios para entender.

Expertos por favor agregame / corrigeme

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


IEnumerable implementa GetEnumerator. Cuando se le llama, ese método devolverá un IEnumerator que implementa MoveNext, Reset y Current.

Por lo tanto, cuando su clase implementa IEnumerable, está diciendo que puede llamar a un método (GetEnumerator) y obtener un nuevo objeto devuelto (un IEnumerator) que puede usar en un bucle como foreach.


Diferencias entre IEnumerable y IEnumerator:

  • IEnumerable utiliza IEnumerator internamente.
  • IEnumerable no sabe qué elemento / objeto se está ejecutando.
  • Cada vez que pasamos IEnumerator a otra función, conoce la posición actual del elemento / objeto.
  • Cada vez que pasamos la colección IEnumerable a otra función, no conoce la posición actual del elemento / objeto (no sabe qué elemento está ejecutando)

    IEnumerable tiene un método GetEnumerator ()

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

IEnumerator tiene una propiedad actual y dos métodos Reset y MoveNext (que es útil para conocer la posición actual de un elemento en una lista).

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


Las interfaces IEnumerable e IEnumerator

Para comenzar a examinar el proceso de implementación de las interfaces .NET existentes, veamos primero la función de IEnumerable e IEnumerator. Recuerde que C # admite una palabra clave llamada foreach que le permite iterar sobre el contenido de cualquier tipo de matriz:

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

Si bien puede parecer que solo los tipos de matriz pueden hacer uso de este constructo, la verdad del asunto es que cualquier tipo que admita un método llamado GetEnumerator () puede ser evaluado por el constructo foreach. Para ilustrarlo, ¡sígame!

Supongamos que tenemos una clase de garaje:

// 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, sería conveniente iterar sobre los subelementos del objeto de Garage utilizando la construcción foreach, al igual que una matriz de valores de datos:

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

Lamentablemente, el compilador le informa que la clase de Garage no implementa un método llamado GetEnumerator (). Este método se formaliza mediante la interfaz IEnumerable, que se encuentra al acecho dentro del espacio de nombres System.Collections. Las clases o estructuras que admiten este comportamiento anuncian que pueden exponer subelementos contenidos a la persona que llama (en este ejemplo, la propia palabra clave foreach). Aquí está la definición de esta interfaz estándar .NET:

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

Como puede ver, el método GetEnumerator () devuelve una referencia a otra interfaz llamada System.Collections.IEnumerator. Esta interfaz proporciona la infraestructura para permitir que la persona que llama atraviese los objetos internos contenidos en el contenedor compatible 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. }

Si desea actualizar el tipo de Garage para que sea compatible con estas interfaces, puede tomar el camino largo e implementar cada método manualmente. Si bien es cierto que es libre de proporcionar versiones personalizadas de GetEnumerator (), MoveNext (), Current y Reset (), hay una forma más sencilla. Como el tipo System.Array (así como muchas otras clases de colección) ya implementa IEnumerable e IEnumerator, simplemente puede delegar la solicitud a System.Array de la siguiente manera:

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

Una vez que haya actualizado su tipo de garaje, puede usar de manera segura el tipo dentro de la construcción de C # foreach. Además, dado que el método GetEnumerator () se ha definido públicamente, el usuario del objeto también podría interactuar con el 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);

Sin embargo, si prefiere ocultar la funcionalidad de IEnumerable del nivel de objeto, simplemente haga uso de la implementación de la interfaz explícita:

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

Al hacerlo, el usuario del objeto casual no encontrará el método GetEnumerator () de Garage, mientras que la construcción foreach obtendrá la interfaz en segundo plano cuando sea necesario.

Adaptado de Pro C # 5.0 y .NET 4.5 Framework


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 */


  • Un objeto que implementa IEnumerable permite que otros visiten cada uno de sus elementos (por un enumerador) .
  • Un objeto que implementa IEnumerator es hacer la iteración. Está en bucle sobre un objeto enumerable.

Piense en objetos enumerables como en listas, pilas, árboles.