2010-05-19 6 views
8

Ich brauche eine einfache Möglichkeit, über mehrere Sammlungen zu iterieren, ohne sie tatsächlich zusammenzuführen, und ich konnte nichts in .NET eingebaut finden, das so aussieht. Es fühlt sich an, als ob dies eine etwas gewöhnliche Situation sein sollte. Ich will das Rad nicht neu erfinden. Gibt es etwas eingebaut, das in etwa so funktioniert:Hat .NET ein IEnumerable für mehrere Sammlungen?

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

} 

Antwort

15

Probieren Sie die SelectMany Extension-Methode in 3.5 hinzugefügt.

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

Der Code SelectMany(x => x) die Wirkung hat, eine Sammlung von Sammlungen in einer einzigen Sammlung Abflachung. Dies wird auf eine faule Art und Weise durchgeführt und ermöglicht eine einfache Verarbeitung, wie oben gezeigt.

Wenn nur C# 2.0 verfügbar ist, können Sie einen Iterator verwenden, um die gleichen Ergebnisse zu erzielen.

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

Ich könnte falsch liegen, aber ich glaube nicht, dass das Schlüsselwort 'var' in C# 2.0 verfügbar ist. –

+0

@Dan Sie haben Recht, kein var Schlüsselwort in C# 2.0. Es wäre kürzer, den Typparameter T trotzdem zu verwenden. Einfache Lösung. –

+2

Es ist nicht im C# 2.0-Compiler verfügbar, aber Sie können VS 2008 oder 2010 verwenden, um .NET 2.0 mit Code zu adressieren, der das Schlüsselwort 'var' verwendet und während der Kompilierung in einen tatsächlichen Typ aufgelöst wird. –

10

verwenden einfach die Enumerable.Concat() Erweiterungsmethode auf zwei IEnumerables "Verketten". Keine Sorge, es kopiert sie nicht wirklich in ein einziges Array (wie Sie aus dem Namen entnehmen können), es erlaubt Ihnen einfach, über sie alle aufzuzählen, als wären sie ein IEnumerable.

Wenn Sie mehr als zwei haben, dann Enumerable.SelectMany() wäre besser.

+0

'var list = list1.Concat (list2) .Concat (ienerable3) .Concat (array4); 'ist eine nette prägnante Möglichkeit, dies zu tun, und der Bonus ist, dass Sie es über verschiedene Sammlungstypen tun können, solange sie alle den gleichen Typparameter haben. –

+0

Oder verwenden Sie Union (http://msdn.microsoft.com/en-us/library/bb341731.aspx), wenn Sie Dubletten vermeiden müssen ... – Reddog

Verwandte Themen