Лучшее решение - подождать несколько месяцев, пока IAsyncEnumerable<T>
не станет реальностью.Следующее лучшее решение - использовать IAsyncEnumerable<T>
из System.Interactive.Async .Но пока ...
IEnumerable<T>
- итератор синхронного извлечения.Поэтому он должен предоставить свои T
экземпляры синхронно.Более конкретно, для этого вопроса он должен предоставить count экземпляров синхронно.
Task<T>
- асинхронное извлечение одного элемента.Он будет предоставлять только свой T
экземпляр асинхронно.
Так вот в чем проблема: ваш желаемый результат IEnumerable<Task<T>>
должен иметь возможность:
- Предоставлять каждый
Task<T>
синхронно.Не слишком сложно;Вы можете использовать TaskCompletionSource<T>
, если это абсолютно необходимо. - Обеспечить count синхронно.Другими словами, необходимо обеспечить каждые
Task<T>
синхронно.Все они.Это невозможно, учитывая ваш ввод.
Ваш ввод IEnumerable<Task<T[]>>
.Поскольку это IEnumerable<>
, вы можете получить счетчик синхронно, но это только количество элементов Task<T[]>
, то есть количество элементов T[]
.Чтобы выполнить операцию сглаживания, вам потребуется await
каждый из этих Task<T[]>
элементов, чтобы получить количество T
элементов.Таким образом, вы не можете создать IEnumerable<Task<T>>
, который может синхронно знать, сколько у него предметов.
Вы сталкиваетесь с этим ограничением типа, потому что IEnumerable<Task<T>>
равно , а не так же, как IAsyncEnumerable<T>
.
Возможны следующие варианты:
- Используйте реальное
IAsyncEnumerable<T>
, написанное самостоятельно или через System.Interactive.Async.Плюсы: правильные типы допускают семантически точный код.Минусы: производство и потребление IAsyncEnumerable<T>
по-прежнему является проблемой (пока). - Выполните всю асинхронную работу заранее.Плюсы: проще код.Минусы: изменяет желаемую семантику.
При втором варианте ваш оператор сглаживания может выглядеть так:
public static async Task<IEnumerable<T>> Flatten<T>(IEnumerable<Task<T[]>> tasks)
{
var results = await Task.WhenAll(tasks);
return results.SelectMany(x => x);
}
Пока у вас не будет истинного IAsyncEnumerable<T>
, многие операторы будутпотребовать, чтобы будущее (Task<T>
) закончилось снаружи вот так.