2010-05-19 4 views
8

Necesito una manera fácil de iterar sobre varias colecciones sin realmente fusionarlas, y no pude encontrar nada integrado en .NET que parece que sí lo hace. Parece que esto debería ser una situación algo común. No quiero reinventar la rueda. ¿Hay algo incorporado que haga algo como esto?¿.NET tiene un IEnumerable integrado para múltiples colecciones?

public class MultiCollectionEnumerable<T> : IEnumerable<T> 
{ 
    private MultiCollectionEnumerator<T> enumerator; 
    public MultiCollectionEnumerable(params IEnumerable<T>[] collections) 
    { 
     enumerator = new MultiCollectionEnumerator<T>(collections); 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     enumerator.Reset(); 
     return enumerator; 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     enumerator.Reset(); 
     return enumerator; 
    } 


    private class MultiCollectionEnumerator<T> : IEnumerator<T> 
    { 
     private IEnumerable<T>[] collections; 
     private int currentIndex; 
     private IEnumerator<T> currentEnumerator; 

     public MultiCollectionEnumerator(IEnumerable<T>[] collections) 
     { 
      this.collections = collections; 
      this.currentIndex = -1; 
     } 

     public T Current 
     { 
      get 
      { 
       if (currentEnumerator != null) 
        return currentEnumerator.Current; 
       else 
        return default(T); 
      } 
     } 

     public void Dispose() 
     { 
      if (currentEnumerator != null) 
       currentEnumerator.Dispose(); 
     } 

     object IEnumerator.Current 
     { 
      get 
      { 
       return Current; 
      } 
     } 

     public bool MoveNext() 
     { 
      if (currentIndex >= collections.Length) 
       return false; 
      if (currentIndex < 0) 
      { 
       currentIndex = 0; 
       if (collections.Length > 0) 
        currentEnumerator = collections[0].GetEnumerator(); 
       else 
        return false; 
      } 
      while (!currentEnumerator.MoveNext()) 
      { 
       currentEnumerator.Dispose(); 
       currentEnumerator = null; 

       currentIndex++; 
       if (currentIndex >= collections.Length) 
        return false; 
       currentEnumerator = collections[currentIndex].GetEnumerator(); 
      } 
      return true; 
     } 

     public void Reset() 
     { 
      if (currentEnumerator != null) 
      { 
       currentEnumerator.Dispose(); 
       currentEnumerator = null; 
      } 
      this.currentIndex = -1; 
     } 
    } 

} 

Respuesta

15

Pruebe el método de extensión SelectMany agregado en 3.5.

IEnumerable<IEnumerable<int>> e = ...; 
foreach (int cur in e.SelectMany(x => x)) { 
    Console.WriteLine(cur); 
} 

El código SelectMany(x => x) tiene el efecto de aplanar una colección de colecciones en una sola colección. Esto se hace de una manera perezosa y permite el procesamiento directo como se muestra arriba.

Si solo tiene C# 2.0 disponible, puede usar un iterador para lograr los mismos resultados.

public static IEnumerable<T> Flatten<T>(IEnumerable<IEnumerable<T>> enumerable) { 
    foreach (var inner in enumerable) { 
    foreach (var value in inner) { 
     yield return value; 
    } 
    } 
} 
+0

Podría estar equivocado, pero no creo que la palabra clave 'var' esté disponible en C# 2.0. –

+0

@Dan Tienes razón, no hay palabra clave var en C# 2.0. Sería más corto usar el parámetro de tipo T de todos modos. Solución fácil –

+2

No está disponible en el compilador C# 2.0, pero puede usar VS 2008 o 2010 para apuntar .NET 2.0 con código que usa la palabra clave 'var', que se resuelve en un tipo real durante la compilación, dejando el tiempo de ejecución más acertado. –

10

sólo tiene que utilizar el método Enumerable.Concat() extensión a "concatenar" dos IEnumerables. No se preocupe, en realidad no los copia en una sola matriz (como podría inferir del nombre), simplemente le permite enumerarlos como si fueran uno IEnumerable.

Si tiene más de dos, entonces Enumerable.SelectMany() sería mejor.

+0

'var list = list1.Concat (list2) .Concat (ienumerable3) .Concat (array4); 'es una forma agradable y concisa de hacer esto, y la ventaja es que puedes hacerlo a través de diferentes tipos de colección, siempre que todos tengan el mismo parámetro de tipo. –

+0

O use Union (http://msdn.microsoft.com/en-us/library/bb341731.aspx) si necesita desalentar duplicados ... – Reddog

Cuestiones relacionadas