Мне нужен простой способ перебора нескольких коллекций без их фактического слияния, и я не смог найти ничего встроенного в .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;
}
}
}
Попробуйте метод расширения 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;
}
}
}
Просто используйте метод расширения Enumerable.Concat()
для "конкатенации" двух IEnumerables. Не волнуйтесь, на самом деле он не копирует их в один массив (как можно предположить из названия), а просто позволяет перечислить их все, как если бы они были одним IEnumerable.
Если у вас их больше двух, то Enumerable.SelectMany()
будет лучше.