Вы должны знать, что такое объект Enumerable.Перечислимый объект, не само перечисление.Это дает вам возможность получить объект Enumerator.Если у вас есть объект Enumerator, вы можете запросить первые элементы последовательности, а после того, как у вас есть элемент перечисления, вы можете запросить следующий.
Итак, создание объекта, который позволяет вамдля перечисления по последовательности не имеет ничего общего с самим перечислением.
Ваша функция Select возвращает только объект Enumerable, но не запускает перечисление.Перечисление начинается с ToArray
.
. На самом низком уровне перечисление выполняется следующим образом:
IEnumerable<TSource> myEnumerable = ...
IEnumerator<TSource> enumerator = myEnumerable.GetEnumerator();
while (enumerator.MoveNext())
{
// there is still an element in the enumeration
TSource currentElement = enumerator.Current;
Process(currentElement);
}
ToArray
вызовет внутренние вызовы GetEnumerator
и MoveNext
.Поэтому, чтобы сделать оператор LINQ асинхронным, вам понадобится ToArrayAsync
.
Код довольно прост.Я покажу вам ToListAsync как метод расширения, который немного проще сделать.
static class EnumerableAsyncExtensions
{
public static async Task<List<TSource>> ToListAsync<TSource>(
this IEnumerable<Task<TSource>> source)
{
List<TSource> result = new List<TSource>();
var enumerator = source.GetEnumerator()
while (enumerator.MoveNext())
{
// in baby steps. Feel free to do this in one step
Task<TSource> current = enumerator.Current;
TSource awaitedCurrent = await current;
result.Add(awaitedCurrent);
}
return result;
}
}
Вам нужно будет создать его только один раз.Вы можете использовать его для любого ToListAsync, где вам придется ожидать каждого элемента:
var result = Enumerable.Range(0, 3)
.Select(i => TestMethod(i))
.ToListAsync();
Обратите внимание, что возвращение Select
равно IEnumerable<Task<int>>
: объект, который позволяет перечислять последовательностьTask<int>
объектов.В каждом цикле вы получаете Task<int>