Есть ли в .NET встроенный IEnumerable для нескольких коллекций?

Мне нужен простой способ перебора нескольких коллекций без их фактического слияния, и я не смог найти ничего встроенного в .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;
        }
    }

}
8
задан Bryce Wagner 19 May 2010 в 00:10
поделиться

2 ответа

Попробуйте метод расширения SelectMany, добавленный в 3.5.

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;
    }
  }
}
16
ответ дан 5 December 2019 в 06:22
поделиться

Просто используйте метод расширения Enumerable.Concat() для "конкатенации" двух IEnumerables. Не волнуйтесь, на самом деле он не копирует их в один массив (как можно предположить из названия), а просто позволяет перечислить их все, как если бы они были одним IEnumerable.

Если у вас их больше двух, то Enumerable.SelectMany() будет лучше.

10
ответ дан 5 December 2019 в 06:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: