【问题标题】:Does .NET have a built in IEnumerable for multiple collections?.NET 是否有用于多个集合的内置 IEnumerable?
【发布时间】:2010-05-19 00:10:39
【问题描述】:

我需要一种简单的方法来迭代多个集合而不实际合并它们,而且我找不到任何内置于 .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;
        }
    }

}

【问题讨论】:

    标签: c# .net ienumerable


    【解决方案1】:

    尝试 3.5 中添加的 SelectMany 扩展方法。

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

    代码SelectMany(x =&gt; 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;
        }
      }
    }
    

    【讨论】:

    • 我可能是错的,但我认为 var 关键字在 C# 2.0 中不可用。
    • @Dan 你说得对,C# 2.0 中没有 var 关键字。无论如何使用类型参数 T 会更短。轻松修复。
    • 它在 C# 2.0 编译器中不可用,但您可以使用 VS 2008 或 2010 使用使用 var 关键字的代码来定位 .NET 2.0,该关键字在编译期间被解析为实际类型,让运行时变得更明智。
    • "var" 在 Visual Studio 2008 及更高版本中可用,与目标 .NET 版本无关。
    • @Joel:是的……在我工作的那段时间里,我们还在 VS 2005 上,所以我通常会尝试迎合“如果你还在 C# 2.0 / .NET 2.0... “对那艘特定船上的人的评论。无论如何,正如 Jamie 指出的那样,将 var 更改为 T 完全是微不足道的。 (这些 cmets 主要是为了 OP [EDIT: ...他们显然已经知道这一切!]。)
    【解决方案2】:

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

    如果你有两个以上,那么Enumerable.SelectMany() 会更好。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-28
    • 2012-01-24
    • 2010-09-07
    • 1970-01-01
    • 2011-04-23
    • 2010-12-23
    • 2011-11-02
    • 2010-11-23
    相关资源
    最近更新 更多