2010-05-19 17 views
8

Potrzebuję łatwego sposobu na iterowanie po wielu kolekcjach bez ich scalania, a ja nie mogłem znaleźć niczego wbudowanego w .NET, który wygląda tak, jak to robi. Wydaje się, że taka sytuacja powinna być dość powszechna. Nie chcę wymyślać koła. Czy coś jest wbudowane w coś takiego:Czy .NET ma wbudowany IEnumerable dla wielu kolekcji?

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

} 

Odpowiedz

15

Wypróbuj metodę SelectMany dodaną w 3.5.

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

Kod SelectMany(x => x) skutkuje spłaszczeniem kolekcję zbiorów w jednej kolekcji. Odbywa się to w sposób leniwy i pozwala na proste przetwarzanie, jak pokazano powyżej.

Jeśli dostępne jest tylko C# 2.0, można użyć iteratora, aby uzyskać takie same wyniki.

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

Mogę się mylić, ale nie sądzę, słowo kluczowe "var" jest dostępny w C# 2.0. –

+0

@Dan Masz rację, nie ma słowa kluczowego var w C# 2.0. Byłoby krótsze, aby użyć parametru typu T mimo to. Łatwa naprawa. –

+2

Nie jest dostępny w kompilatorze C# 2.0, ale można użyć VS 2008 lub 2010 w celu kierowania .NET 2.0 z kodem, który używa słowa kluczowego 'var', który zostanie rozwiązany do rzeczywistego typu podczas kompilacji, pozostawiając środowisko wykonawcze o wiele mądrzejsze. –

10

Wystarczy użyć metody Enumerable.Concat() przedłużającego się "łączyć" dwa IEnumerables. Nie przejmuj się, nie kopiuje ich w jedną tablicę (jak można wywnioskować z nazwy), po prostu pozwala ci wyliczyć wszystkie z nich tak, jakby były jednym z nich.

Jeśli masz więcej niż dwa, wtedy Enumerable.SelectMany() będzie lepiej.

+0

'var list = list1.Concat (list2) .Concat (ienumerable3) .Concat (array4); 'jest ładnym, zwięzłym sposobem na zrobienie tego, a premia polega na tym, że możesz to zrobić w różnych typach kolekcji, o ile wszystkie mają ten sam typ parametru. –

+0

Lub użyj Unii (http://msdn.microsoft.com/en-us/library/bb341731.aspx), jeśli chcesz zniechęcić do duplikatów ... – Reddog

Powiązane problemy