read getenumerator ejemplo c# foreach enumerable enumerator

getenumerator - read ienumerable c#



C#IEnumerable, la funciĆ³n de reinicio de IEnumerator no se llama (4)

Cada vez que se foreach , se solicita un nuevo IEnumerator . Devolver su instancia de clase es una mala idea: debe crear una clase separada para implementar IEnumerator y devolverla en su lugar.

Esto a menudo se hace utilizando una clase anidada (privada) y devolviendo una instancia de la misma. Puede pasar la instancia de clase A a la clase privada (dándole acceso a data ) y colocar el campo de position en esa clase. Permitiría crear más de un enumerador simultáneamente, y funcionaría correctamente con las llamadas foreach posteriores.

Por ejemplo, para modificar tu código, harías algo como:

using System; using System.Collections; class A : IEnumerable { int[] data = { 0, 1, 2, 3, 4 }; public IEnumerator GetEnumerator() { return new AEnumerator(this); } private class AEnumerator : IEnumerator { public AEnumerator(A inst) { this.instance = inst; } private A instance; private int position = -1; public object Current { get { return instance.data[position]; } } public bool MoveNext() { position++; return (position < instance.data.Length); } public void Reset() { position = -1; } } }

Tenga en cuenta que también puede simplemente devolver el enumerador de la matriz directamente (aunque suponía que estaba tratando de aprender cómo hacer enumeradores limpios):

class A : IEnumerable { int[] data = { 0, 1, 2, 3, 4 }; public IEnumerator GetEnumerator() { return data.GetEnumerator(); } }

Finalmente, puede usar iteradores para implementar esto de una manera mucho más simple:

class A : IEnumerable { int[] data = { 0, 1, 2, 3, 4 }; public IEnumerator GetEnumerator() { for (int i=0;i<data.Length;++i) yield return data[i]; } }

Una vez dicho esto, recomiendo implementar IEnumerable<int> además de IEnumerable. Los genéricos hacen que esto sea mucho mejor en términos de uso.

Básicamente estoy tratando de hacer que mi clase pueda iterar usando foreach . Leí este tutorial. MSDN . Parece muy sencillo. Sin embargo, tengo un problema cuando quiero iterar por segunda vez. Lo depuré; y resultó que no llama a la función Reset() .

Clase A

class A : IEnumerable, IEnumerator { int[] data = { 0, 1, 2, 3, 4 }; int position = -1; public object Current { get { return data[position]; } } public bool MoveNext() { position++; return (position < data.Length); } public void Reset() { position = -1; } public IEnumerator GetEnumerator() { return (IEnumerator)this; } }

Cuando ejecuto la siguiente función principal; nunca llama a la función Reset() . Entonces, después de un ciclo, nunca podré repetir mi clase nuevamente.

Principal

static void Main(string[] args) { A a = new A(); foreach (var item in a) { Console.WriteLine(item); } Console.WriteLine("--- First foreach finished. ---"); foreach (var item in a) { Console.WriteLine(item); } }

Salida:

0 1 2 3 4 --- First foreach finished. --- Press any key to continue . . .

¿Alguna idea?


La enumeración no llama Reset . Necesita crear una nueva instancia de un enumerador, lo que probablemente signifique crear una clase separada para el enumerador (es decir, no usar el mismo tipo para IEnumerable e IEnumerator), como en el siguiente código:

public class _11475328 { class A : IEnumerable { int[] data = { 0, 1, 2, 3, 4 }; public IEnumerator GetEnumerator() { return new AEnumerator(this); } class AEnumerator : IEnumerator { private A parent; private int position = -1; public AEnumerator(A parent) { this.parent = parent; } public object Current { get { return parent.data[position]; } } public bool MoveNext() { position++; return (position < parent.data.Length); } public void Reset() { position = -1; } } } public static void Test() { A a = new A(); foreach (var item in a) { Console.WriteLine(item); } Console.WriteLine("--- First foreach finished. ---"); foreach (var item in a) { Console.WriteLine(item); } } }


Reset () fue básicamente un error. Ya existe un método conocido para obtener un enumerador limpio si es posible: GetEnumerator ().

Es un requisito en la especificación que las implementaciones de bloque de iterador (para el iterador) arrojen una excepción para este método, por lo tanto, en el caso general se sabe formalmente que no se puede esperar que funcione, y simplemente: nadie lo llama. Francamente, ¡también deberían haberlo marcado [Obsoleto] en la API!

Además, muchas secuencias no son repetibles. Piense en los iteradores sentados en un NetworkStream o en un generador de números aleatorios. Debido a que no es necesario que los iteradores (en el caso general) sean repetibles, debe apuntar, cuando sea posible, a iterarlos como máximo una vez. Tal vez el almacenamiento en búfer a través de ToList () si eso no es posible.

"foreach" no implica Reset () en ningún punto. Solo GetEnumerator (), MoveNext (), Current y Dispose ().


Tanto Reed como Carlos están en lo correcto. Aquí hay una forma de hacerlo, ya que int[] implementa IEnumerable e IEnumerable<int>

class A : IEnumerable { int[] data = { 0, 1, 2, 3, 4 }; public IEnumerator GetEnumerator() { return data.GetEnumerator(); } }

O, para escribir con más fuerza, puede usar la forma genérica de IEnumerable :

class A : IEnumerable<int> { int[] data = { 0, 1, 2, 3, 4 }; public IEnumerator<int> GetEnumerator() { return ((IEnumerable<int>)data).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return data.GetEnumerator(); } }