que - yield c#




¿Alguien puede explicarme IEnumerable y IEnumerator? (10)

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 ) .

¿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?


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).


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 .


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.


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

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.





ienumerator