2010-05-19 96 views
8

我需要一個簡單的方法來迭代多個集合而不實際合併它們,而且我找不到任何內置到.NET中的內容,看起來像是這樣。感覺這應該是一種常見的情況。我不想重新發明輪子。有沒有內置的東西可以做這樣的事情:.NET是否有一個內置的IEnumerable用於多個集合?

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

} 

回答

15

嘗試使用3.5中添加的SelectMany擴展方法。

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

代碼SelectMany(x => x)具有將集合集合展平爲單個集合的效果。這是以懶惰的方式完成的,並且允許如上所示的直接處理。

如果您只有C#2.0可用,則可以使用迭代器實現相同的結果。

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

我可能是錯的,但我不認爲'var'關鍵字在C#2.0中可用。 – 2010-05-19 00:21:26

+0

@Dan你是對的,C#2.0中沒有var關鍵字。無論如何,使用類型參數T會更短。輕鬆修復。 – 2010-05-19 00:23:50

+2

它在C#2.0編譯器中不可用,但是您可以使用VS 2008或2010將.NET 2.0作爲使用'var'關鍵字的代碼的目標,在編譯過程中將其解析爲實際類型,從而使運行時間不會更明智。 – 2010-05-19 00:24:21

10

只需使用Enumerable.Concat()擴展方法「連接」兩個IEnumerables。不要擔心,它實際上並沒有將它們複製到一個數組中(正如您可能從名稱推斷的那樣),它只是允許您枚舉它們,就像它們是一個IEnumerable一樣。

如果你有兩個以上的話Enumerable.SelectMany()會更好。

+0

'var list = list1.Concat(list2).Concat(ienumerable3).Concat (array4);'是一個很好的簡潔的方法來做到這一點,而且可以通過不同的集合類型來完成,只要它們具有相同的類型參數即可。 – 2010-05-19 00:25:40

+0

如果您需要阻止重複項,請使用Union(http://msdn.microsoft.com/zh-cn/library/bb341731.aspx)... – Reddog 2010-05-19 00:25:40

相關問題