c# enumeration infinite-loop yield-return

c# - ¿Cómo manejar un "infinito" IEnumerable?



enumeration infinite-loop (5)

Sí, su código siempre funcionará sin bucles infinitos. Alguien podría venir más tarde y arruinar las cosas. Supongamos que quieren hacer:

var q = Numbers().ToList();

¡Entonces, estás decidido! Muchas funciones "agregadas" te matarán, como Max() .

Un ejemplo trivial de un IEnumerable "infinito" sería

IEnumerable<int> Numbers() { int i=0; while(true) { yield return unchecked(i++); } }

Yo sé eso

foreach(int i in Numbers().Take(10)) { Console.WriteLine(i); }

y

var q = Numbers(); foreach(int i in q.Take(10)) { Console.WriteLine(i); }

ambos funcionan bien (e imprimen el número 0-9).

Pero, ¿existen errores al copiar o manejar expresiones como q ? ¿Puedo confiar en el hecho de que siempre son evaluados "perezosos"? ¿Hay algún peligro para producir un bucle infinito?


Sí, tiene la garantía de que el código anterior se ejecutará perezosamente. Si bien parece (en tu código) como si estuvieras en un bucle para siempre, tu código produce algo como esto:

IEnumerable<int> Numbers() { return new PrivateNumbersEnumerable(); } private class PrivateNumbersEnumerable : IEnumerable<int> { public IEnumerator<int> GetEnumerator() { return new PrivateNumbersEnumerator(); } } private class PrivateNumbersEnumerator : IEnumerator<int> { private int i; public bool MoveNext() { i++; return true; } public int Current { get { return i; } } }

(Obviamente, esto no es exactamente lo que se generará, ya que es bastante específico para su código, pero aún así es similar y debería mostrarle por qué se va a evaluar perezosamente).


Si no fue una evaluación perezosa, su primer ejemplo no funcionará como se esperaba en primer lugar.


Siempre y cuando solo llames a métodos perezosos, sin búfer, deberías estar bien. Así que Skip , Take , Select , etc. están bien. Sin embargo, Min , Count , OrderBy , etc. se volverían locos.

Puede funcionar, pero debes ser cauteloso. O inyecte un Take(somethingFinite) como medida de seguridad (o algún otro método de extensión personalizado que arroje una excepción después de demasiados datos).

Por ejemplo:

public static IEnumerable<T> SanityCheck<T>(this IEnumerable<T> data, int max) { int i = 0; foreach(T item in data) { if(++i >= max) throw new InvalidOperationException(); yield return item; } }


Tendrías que evitar cualquier función codiciosa que intente leer para terminar. Esto incluiría extensiones ToArray como: Count , ToArray / ToList , y agregados Avg / Min / Max , etc.

No hay nada de malo con las listas perezosas infinitas, pero debes tomar decisiones conscientes sobre cómo manejarlas.

Use Take para limitar el impacto de un bucle sin fin estableciendo un límite superior incluso si no los necesita todos.