.NET是否有适用于多个集合的内置IEnumerable?

9

我需要一种简单的方法来迭代多个集合,而不实际合并它们,但我在.NET中找不到任何内置的东西看起来像它这样做。感觉这应该是一个比较常见的情况。我不想重复发明轮子。有没有内置的东西可以做到这样的事情:

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

}
2个回答

16

尝试使用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;
    }
  }
}

我可能错了,但我认为在C# 2.0中没有var关键字。 - Dan Tao
@Dan 你说得对,C# 2.0 中没有 var 关键字。使用类型参数 T 会更简洁。很容易解决。 - Jamie Penney
2
在 C# 2.0 编译器中不可用,但您可以使用 VS 2008 或 2010 来针对 .NET 2.0 进行编码,其中使用 var 关键字,在编译期间将其解析为实际类型,运行时则毫不知情。 - Joel Mueller
“var” 在 Visual Studio 2008 及更高版本中可用,无论目标 .NET 版本如何。 - Bryce Wagner
@Joel: 真的...在我工作的一段时间里,我们还在使用VS 2005,所以我通常会尝试为那些处于特定情况的人提供“如果你仍然在使用C# 2.0 / .NET 2.0…”的建议。不过,正如Jamie指出的那样,将var更改为T是完全微不足道的。(这些评论主要是为了受益于OP [EDIT:...显然他已经知道所有这些!])。 - Dan Tao
对于我的情况,yield return 解决方案效果更好。我很清楚 yield return 的作用,并且以前也用过它,但是使用频率不够高,所以有时会忘记它的存在。使用 yield return 可以将 IEnumerable<T1> 和 IEnumerable<T2> 合并为 IEnumerable<T>,其中 T1 和 T2 都是从 T 派生而来的,而无需升级到 .NET 4。 - Bryce Wagner

10

只需使用Enumerable.Concat()扩展方法来“连接”两个IEnumerables。不用担心,它实际上并不会将它们复制到单个数组中(从名称中可能会推断出这一点),它只是简单地允许您枚举它们所有的元素,就好像它们是一个IEnumerable一样。

如果您有多个,则最好使用Enumerable.SelectMany()


var list = list1.Concat(list2).Concat(ienumerable3).Concat(array4); 这是一个很好的简洁方式来实现这一点,而且额外的好处是,只要所有集合类型都具有相同的类型参数,你就可以在不同的集合类型上进行操作。 - Jamie Penney
如果您需要避免重复项,可以使用Union(http://msdn.microsoft.com/en-us/library/bb341731.aspx)。 - Reddog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接