2010-08-26 19 views

Respuesta

16

yield return es un operador bastante útil para esto, aunque en realidad no requiere específicamente LINQ.

IEnumerable<int> GetInfiniteSeries(IEnumerable<int> items) { 
    while (true) { 
     foreach (var item in items) { 
      yield return item; 
     } 
    } 
} 
+1

mucho más corto que mi versión :) –

6
IEnumerable<T> Infinite(this IEnumerable<T> ienum) 
{ 
    List<T> list = ienum.ToList(); 
    while (true) 
     foreach(var t in list) 
      yield return t; 
} 



foreach(int i in Enumerable.Range(1,3).Infinite()) 
     Console.WriteLine(i); 
+0

es la llamada a 'ToList()' es necesario? –

+0

+1: 2 problemas: 1. Esto no funcionará con 'ienum.Infinite(). Infinite()', cuando lógicamente una implementación 'Infinite()' debería soportar esto. 2. Si descuidamos el punto 1, hay un problema de rendimiento: el enumerador sigue siendo recreado y eliminado. Sería mucho mejor que se rewitten como un bucle for que se restablece a '0' cuando toca' list.Count'. La otra alternativa es confiar en 'IEnumerator.Reset()', pero supongo que es peligroso ya que muchas implementaciones no lo admiten. – Ani

+2

@Ani: ¿Cómo sabes que hay un problema de rendimiento? ¿Sabe sin evidencia empírica que la creación y destrucción de un iterador de lista -una estructura diseñada específicamente por el equipo de BCL para ser increíblemente rápida de asignar y eliminar- es la cosa * más lenta * en la aplicación del usuario? ¿O hizo un extenso y cuidadoso trabajo de creación de perfiles para determinar que la asignación y eliminación de esta estructura es, de hecho, el mayor problema de rendimiento en la aplicación del usuario? Si es así, me gustaría ver esa información para poder transmitirla al equipo de rendimiento de BCL, ¡gracias! –

3

Así es como lo he hecho con el tiempo:

public static IEnumerable<T> AdNauseam<T>(this IEnumerable<T> i_list) 
    { 
     using(var etor = i_list.GetEnumerator()) 
     { 
      while(true) 
      { 
       while(etor.MoveNext()) 
       { 
        yield return etor.Current; 
       } 
       etor.Reset(); 
      } 
     } 
    } 

Uso:

var list = new[] {1, 2, 3} 
var infinite = list.AdNauseam().Take(10); 

El resultado:

{1, 2, 3, 1, 2, 3, 1, 2, 3, 1} 
+0

Me pregunto si el uso de() es útil en este caso. –

+1

El uso() es necesario - IEnumerator implementa IDisposable. Para la mayoría de las listas, la disposición puede no hacer mucho, pero si el enumerador estaba haciendo algo novedoso, como leer un archivo o una base de datos, desearía que se eliminara. –

+2

Me pregunto si el uso de Restablecer es la mejor opción dado que 'El método Restablecer se proporciona para la interoperabilidad COM. No necesariamente necesita ser implementado; en cambio, el implementador puede simplemente lanzar una NotSupportedException. ' (fuente: http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset(v=vs.110).aspx) –

2

Otra opción, implementar IEnumerator<T>:

public class InfiniteEnumerator<T> : IEnumerator<T> 
    { 
     private IList<T> _items; 
     private int _index = -1; 

     public InfiniteEnumerator(IList<T> items) 
     { 
      if (items == null) 
      { 
       throw new ArgumentNullException("items"); 
      } 
      _items = items; 
     } 

     public T Current 
     { 
      get { return _items[_index]; } 
     } 

     public void Dispose() 
     { 

     } 

     object System.Collections.IEnumerator.Current 
     { 
      get { return _items[_index]; } 
     } 

     public bool MoveNext() 
     { 
      if (_items.Count == 0) 
      { 
       return false; 
      } 

      _index = (_index + 1) % _items.Count; 
      return true; 
     } 

     public void Reset() 
     { 
      _index = -1; 
     } 
    } 
+1

Prefiero esta implementación ya que es más descriptiva de lo que realmente está haciendo: enumerar la lista indefinidamente. Se siente mucho mejor que la idea de un IEnumerable infinito, y como mencionó Ani, evita el cerebro-explosivo que es 'ienum.Infinito(). Infinito()' – batwad