SelectMany.cs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections.Generic;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. namespace System.Linq
  8. {
  9. #if REFERENCE_ASSEMBLY
  10. public static partial class AsyncEnumerableDeprecated
  11. #else
  12. public static partial class AsyncEnumerable
  13. #endif
  14. {
  15. #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  16. // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.selectmany?view=net-9.0-pp#system-linq-asyncenumerable-selectmany-3(system-collections-generic-iasyncenumerable((-0))-system-func((-0-system-collections-generic-iasyncenumerable((-1))))-system-func((-0-1-2)))
  17. /// <summary>
  18. /// Projects each element of an async-enumerable sequence to an async-enumerable sequence and merges the resulting async-enumerable sequences into one async-enumerable sequence.
  19. /// </summary>
  20. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  21. /// <typeparam name="TResult">The type of the elements in the projected inner sequences and the elements in the merged result sequence.</typeparam>
  22. /// <param name="source">An async-enumerable sequence of elements to project.</param>
  23. /// <param name="selector">A transform function to apply to each element.</param>
  24. /// <returns>An async-enumerable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.</returns>
  25. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> is null.</exception>
  26. public static IAsyncEnumerable<TResult> SelectMany<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, IAsyncEnumerable<TResult>> selector)
  27. {
  28. if (source == null)
  29. throw Error.ArgumentNull(nameof(source));
  30. if (selector == null)
  31. throw Error.ArgumentNull(nameof(selector));
  32. return new SelectManyAsyncIterator<TSource, TResult>(source, selector);
  33. }
  34. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  35. // REVIEW: Should we keep these overloads that return ValueTask<IAsyncEnumerable<TResult>>? One could argue the selector is async twice.
  36. /// <summary>
  37. /// Projects each element of an async-enumerable sequence into an async-enumerable sequence and merges the resulting async-enumerable sequences into one async-enumerable sequence.
  38. /// </summary>
  39. /// <typeparam name="TSource">The type of elements in the source sequence.</typeparam>
  40. /// <typeparam name="TResult">The type of elements in the projected inner sequences and the merged result sequence.</typeparam>
  41. /// <param name="source">An async-enumerable sequence of elements to project.</param>
  42. /// <param name="selector">An asynchronous selector function to apply to each element of the source sequence.</param>
  43. /// <returns>An async-enumerable sequence whose elements are the result of invoking the one-to-many transform function on each element of the source sequence and awaiting the result.</returns>
  44. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> is null.</exception>
  45. [GenerateAsyncOverload]
  46. [Obsolete("Use SelectMany. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the SelectManyAwait functionality now exists as overloads of SelectMany. You will need to modify your callback to take an additional CancellationToken argument.")]
  47. private static IAsyncEnumerable<TResult> SelectManyAwaitCore<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<IAsyncEnumerable<TResult>>> selector)
  48. {
  49. if (source == null)
  50. throw Error.ArgumentNull(nameof(source));
  51. if (selector == null)
  52. throw Error.ArgumentNull(nameof(selector));
  53. return new SelectManyAsyncIteratorWithTask<TSource, TResult>(source, selector);
  54. }
  55. #if !NO_DEEP_CANCELLATION
  56. [GenerateAsyncOverload]
  57. [Obsolete("Use SelectMany. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the SelectManyAwaitWithCancellation functionality now exists as overloads of SelectMany.")]
  58. private static IAsyncEnumerable<TResult> SelectManyAwaitWithCancellationCore<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
  59. {
  60. if (source == null)
  61. throw Error.ArgumentNull(nameof(source));
  62. if (selector == null)
  63. throw Error.ArgumentNull(nameof(selector));
  64. return new SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult>(source, selector);
  65. }
  66. #endif
  67. #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  68. // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.selectmany?view=net-9.0-pp#system-linq-asyncenumerable-selectmany-2(system-collections-generic-iasyncenumerable((-0))-system-func((-0-system-int32-system-collections-generic-iasyncenumerable((-1)))))
  69. /// <summary>
  70. /// Projects each element of an async-enumerable sequence to an async-enumerable sequence by incorporating the element's index and merges the resulting async-enumerable sequences into one async-enumerable sequence.
  71. /// </summary>
  72. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  73. /// <typeparam name="TResult">The type of the elements in the projected inner sequences and the elements in the merged result sequence.</typeparam>
  74. /// <param name="source">An async-enumerable sequence of elements to project.</param>
  75. /// <param name="selector">A transform function to apply to each element; the second parameter of the function represents the index of the source element.</param>
  76. /// <returns>An async-enumerable sequence whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.</returns>
  77. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> is null.</exception>
  78. public static IAsyncEnumerable<TResult> SelectMany<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, IAsyncEnumerable<TResult>> selector)
  79. {
  80. if (source == null)
  81. throw Error.ArgumentNull(nameof(source));
  82. if (selector == null)
  83. throw Error.ArgumentNull(nameof(selector));
  84. return Core(source, selector);
  85. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, IAsyncEnumerable<TResult>> selector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  86. {
  87. var index = -1;
  88. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  89. {
  90. checked
  91. {
  92. index++;
  93. }
  94. var inner = selector(element, index);
  95. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  96. {
  97. yield return subElement;
  98. }
  99. }
  100. }
  101. }
  102. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  103. /// <summary>
  104. /// Projects each element of an async-enumerable sequence into an async-enumerable sequence by incorporating the element's index and merges the resulting async-enumerable sequences into an async-enumerable sequence.
  105. /// </summary>
  106. /// <typeparam name="TSource">The type of elements in the source sequence.</typeparam>
  107. /// <typeparam name="TResult">The type of elements in the projected inner sequences and the merged result sequence.</typeparam>
  108. /// <param name="source">An async-enumerable sequence of elements to project.</param>
  109. /// <param name="selector">An asynchronous selector function to apply to each element; the second parameter represents the index of the element.</param>
  110. /// <returns>An async-enumerable sequence who's elements are the result of invoking the one-to-many transform function on each element of the source sequence and awaiting the result.</returns>
  111. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="selector"/> is null.</exception>
  112. [GenerateAsyncOverload]
  113. [Obsolete("Use SelectMany. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the SelectManyAwait functionality now exists as overloads of SelectMany. You will need to modify your callback to take an additional CancellationToken argument.")]
  114. private static IAsyncEnumerable<TResult> SelectManyAwaitCore<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<IAsyncEnumerable<TResult>>> selector)
  115. {
  116. if (source == null)
  117. throw Error.ArgumentNull(nameof(source));
  118. if (selector == null)
  119. throw Error.ArgumentNull(nameof(selector));
  120. return Core(source, selector);
  121. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<IAsyncEnumerable<TResult>>> selector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  122. {
  123. var index = -1;
  124. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  125. {
  126. checked
  127. {
  128. index++;
  129. }
  130. var inner = await selector(element, index).ConfigureAwait(false);
  131. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  132. {
  133. yield return subElement;
  134. }
  135. }
  136. }
  137. }
  138. #if !NO_DEEP_CANCELLATION
  139. [GenerateAsyncOverload]
  140. [Obsolete("Use SelectMany. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the SelectManyAwaitWithCancellation functionality now exists as overloads of SelectMany.")]
  141. private static IAsyncEnumerable<TResult> SelectManyAwaitWithCancellationCore<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
  142. {
  143. if (source == null)
  144. throw Error.ArgumentNull(nameof(source));
  145. if (selector == null)
  146. throw Error.ArgumentNull(nameof(selector));
  147. return Core(source, selector);
  148. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  149. {
  150. var index = -1;
  151. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  152. {
  153. checked
  154. {
  155. index++;
  156. }
  157. var inner = await selector(element, index, cancellationToken).ConfigureAwait(false);
  158. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  159. {
  160. yield return subElement;
  161. }
  162. }
  163. }
  164. }
  165. #endif
  166. #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  167. // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.selectmany?view=net-9.0-pp#system-linq-asyncenumerable-selectmany-3(system-collections-generic-iasyncenumerable((-0))-system-func((-0-system-collections-generic-iasyncenumerable((-1))))-system-func((-0-1-2)))
  168. /// <summary>
  169. /// Projects each element of an async-enumerable sequence to an async-enumerable sequence, invokes the result selector for the source element and each of the corresponding inner sequence's elements, and merges the results into one async-enumerable sequence.
  170. /// </summary>
  171. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  172. /// <typeparam name="TCollection">The type of the elements in the projected intermediate sequences.</typeparam>
  173. /// <typeparam name="TResult">The type of the elements in the result sequence, obtained by using the selector to combine source sequence elements with their corresponding intermediate sequence elements.</typeparam>
  174. /// <param name="source">An async-enumerable sequence of elements to project.</param>
  175. /// <param name="collectionSelector">A transform function to apply to each element.</param>
  176. /// <param name="resultSelector">A transform function to apply to each element of the intermediate sequence.</param>
  177. /// <returns>An async-enumerable sequence whose elements are the result of invoking the one-to-many transform function collectionSelector on each element of the input sequence and then mapping each of those sequence elements and their corresponding source element to a result element.</returns>
  178. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="collectionSelector"/> or <paramref name="resultSelector"/> is null.</exception>
  179. public static IAsyncEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, IAsyncEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
  180. {
  181. if (source == null)
  182. throw Error.ArgumentNull(nameof(source));
  183. if (collectionSelector == null)
  184. throw Error.ArgumentNull(nameof(collectionSelector));
  185. if (resultSelector == null)
  186. throw Error.ArgumentNull(nameof(resultSelector));
  187. return Core(source, collectionSelector, resultSelector);
  188. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, IAsyncEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  189. {
  190. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  191. {
  192. var inner = collectionSelector(element);
  193. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  194. {
  195. yield return resultSelector(element, subElement);
  196. }
  197. }
  198. }
  199. }
  200. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  201. /// <summary>
  202. /// Projects each element of an async-enumerable sequence to an async-enumerable sequence by awaiting the result of a transform function, invokes the result selector for each of the source elements and each of the corrasponding inner-sequence's elements and awaits the result, and merges the results into one async-enumerable sequence.
  203. /// </summary>
  204. /// <typeparam name="TSource">The type of elements in the source sequence.</typeparam>
  205. /// <typeparam name="TCollection">The type of elements in the projected intermediate sequences.</typeparam>
  206. /// <typeparam name="TResult">The type of elements in the result sequence.</typeparam>
  207. /// <param name="source">An async-enumerable sequence of elements to project.</param>
  208. /// <param name="collectionSelector">An asynchronous transform function to apply to each source element.</param>
  209. /// <param name="resultSelector">An asynchronous transform function to apply to each element of the intermediate sequence.</param>
  210. /// <returns>An async-enumerable sequence whose elements are the result of invoking the one-to-many transform function <paramref name="collectionSelector"/> on each element of the input sequence, awaiting the result, applying <paramref name="resultSelector"/> to each element of the intermediate sequences along with their corrasponding source element and awaiting the result.</returns>
  211. /// <exception cref="ArgumentNullException"><paramref name="source"/>, <paramref name="collectionSelector"/>, or <paramref name="resultSelector"/> is <see langword="null"/>.</exception>
  212. [GenerateAsyncOverload]
  213. [Obsolete("Use SelectMany. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the SelectManyAwait functionality now exists as overloads of SelectMany.")]
  214. private static IAsyncEnumerable<TResult> SelectManyAwaitCore<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, ValueTask<TResult>> resultSelector)
  215. {
  216. if (source == null)
  217. throw Error.ArgumentNull(nameof(source));
  218. if (collectionSelector == null)
  219. throw Error.ArgumentNull(nameof(collectionSelector));
  220. if (resultSelector == null)
  221. throw Error.ArgumentNull(nameof(resultSelector));
  222. return Core(source, collectionSelector, resultSelector);
  223. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, ValueTask<TResult>> resultSelector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  224. {
  225. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  226. {
  227. var inner = await collectionSelector(element).ConfigureAwait(false);
  228. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  229. {
  230. yield return await resultSelector(element, subElement).ConfigureAwait(false);
  231. }
  232. }
  233. }
  234. }
  235. #if !NO_DEEP_CANCELLATION
  236. [GenerateAsyncOverload]
  237. [Obsolete("Use SelectMany. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the SelectManyAwaitWithCancellation functionality now exists as overloads of SelectMany.")]
  238. private static IAsyncEnumerable<TResult> SelectManyAwaitWithCancellationCore<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> resultSelector)
  239. {
  240. if (source == null)
  241. throw Error.ArgumentNull(nameof(source));
  242. if (collectionSelector == null)
  243. throw Error.ArgumentNull(nameof(collectionSelector));
  244. if (resultSelector == null)
  245. throw Error.ArgumentNull(nameof(resultSelector));
  246. return Core(source, collectionSelector, resultSelector);
  247. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> resultSelector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  248. {
  249. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  250. {
  251. var inner = await collectionSelector(element, cancellationToken).ConfigureAwait(false);
  252. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  253. {
  254. yield return await resultSelector(element, subElement, cancellationToken).ConfigureAwait(false);
  255. }
  256. }
  257. }
  258. }
  259. #endif
  260. #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  261. // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.selectmany?view=net-9.0-pp#system-linq-asyncenumerable-selectmany-3(system-collections-generic-iasyncenumerable((-0))-system-func((-0-system-int32-system-collections-generic-ienumerable((-1))))-system-func((-0-1-2)))
  262. /// <summary>
  263. /// Projects each element of an async-enumerable sequence to an async-enumerable sequence by incorporating the element's index, invokes the result selector for the source element and each of the corresponding inner sequence's elements, and merges the results into one async-enumerable sequence.
  264. /// </summary>
  265. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  266. /// <typeparam name="TCollection">The type of the elements in the projected intermediate sequences.</typeparam>
  267. /// <typeparam name="TResult">The type of the elements in the result sequence, obtained by using the selector to combine source sequence elements with their corresponding intermediate sequence elements.</typeparam>
  268. /// <param name="source">An async-enumerable sequence of elements to project.</param>
  269. /// <param name="collectionSelector">A transform function to apply to each element; the second parameter of the function represents the index of the source element.</param>
  270. /// <param name="resultSelector">A transform function to apply to each element of the intermediate sequence; the second parameter of the function represents the index of the source element and the fourth parameter represents the index of the intermediate element.</param>
  271. /// <returns>An async-enumerable sequence whose elements are the result of invoking the one-to-many transform function collectionSelector on each element of the input sequence and then mapping each of those sequence elements and their corresponding source element to a result element.</returns>
  272. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="collectionSelector"/> or <paramref name="resultSelector"/> is null.</exception>
  273. public static IAsyncEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, IAsyncEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
  274. {
  275. if (source == null)
  276. throw Error.ArgumentNull(nameof(source));
  277. if (collectionSelector == null)
  278. throw Error.ArgumentNull(nameof(collectionSelector));
  279. if (resultSelector == null)
  280. throw Error.ArgumentNull(nameof(resultSelector));
  281. return Core(source, collectionSelector, resultSelector);
  282. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, IAsyncEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  283. {
  284. var index = -1;
  285. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  286. {
  287. checked
  288. {
  289. index++;
  290. }
  291. var inner = collectionSelector(element, index);
  292. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  293. {
  294. yield return resultSelector(element, subElement);
  295. }
  296. }
  297. }
  298. }
  299. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  300. /// <summary>
  301. /// Projects each element of an async-enumerable sequence to an async-enumerable sequence by awaiting the result of a transform function that incorporates each element's index,
  302. /// invokes the result selector for the source element and each of the corrasponding inner-sequence's elements and awaits the result, and merges the results into one async-enumerable sequence.
  303. /// </summary>
  304. /// <typeparam name="TSource">The type of elements in the source sequence.</typeparam>
  305. /// <typeparam name="TCollection">The type of elements in the projected intermediate sequences.</typeparam>
  306. /// <typeparam name="TResult">The type of elements in the result sequence.</typeparam>
  307. /// <param name="source">An async-enumerable sequence of elements to project.</param>
  308. /// <param name="collectionSelector">An asynchronous transform function to apply to each source element; the second parameter represents the index of the element.</param>
  309. /// <param name="resultSelector">An asynchronous transform function to apply to each element of the intermediate sequence.</param>
  310. /// <returns>An async-enumerable sequence whose elements are the result of invoking the one-to-many transform function <paramref name="collectionSelector"/> on each element of the input sequence, awaiting the result, applying <paramref name="resultSelector"/> to each element of the intermediate sequences olong with their corrasponding source element and awaiting the result.</returns>
  311. /// <exception cref="ArgumentNullException"><paramref name="source"/>, <paramref name="collectionSelector"/>, or <paramref name="resultSelector"/> is <see langword="null"/>.</exception>
  312. [GenerateAsyncOverload]
  313. [Obsolete("Use SelectMany. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the SelectManyAwait functionality now exists as overloads of SelectMany. You will need to modify your callback to take an additional CancellationToken argument.")]
  314. private static IAsyncEnumerable<TResult> SelectManyAwaitCore<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, ValueTask<TResult>> resultSelector)
  315. {
  316. if (source == null)
  317. throw Error.ArgumentNull(nameof(source));
  318. if (collectionSelector == null)
  319. throw Error.ArgumentNull(nameof(collectionSelector));
  320. if (resultSelector == null)
  321. throw Error.ArgumentNull(nameof(resultSelector));
  322. return Core(source, collectionSelector, resultSelector);
  323. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, ValueTask<TResult>> resultSelector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  324. {
  325. var index = -1;
  326. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  327. {
  328. checked
  329. {
  330. index++;
  331. }
  332. var inner = await collectionSelector(element, index).ConfigureAwait(false);
  333. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  334. {
  335. yield return await resultSelector(element, subElement).ConfigureAwait(false);
  336. }
  337. }
  338. }
  339. }
  340. #if !NO_DEEP_CANCELLATION
  341. [GenerateAsyncOverload]
  342. [Obsolete("Use SelectMany. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the SelectManyAwaitWithCancellation functionality now exists as overloads of SelectMany.")]
  343. private static IAsyncEnumerable<TResult> SelectManyAwaitWithCancellationCore<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> resultSelector)
  344. {
  345. if (source == null)
  346. throw Error.ArgumentNull(nameof(source));
  347. if (collectionSelector == null)
  348. throw Error.ArgumentNull(nameof(collectionSelector));
  349. if (resultSelector == null)
  350. throw Error.ArgumentNull(nameof(resultSelector));
  351. return Core(source, collectionSelector, resultSelector);
  352. static async IAsyncEnumerable<TResult> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, CancellationToken, ValueTask<TResult>> resultSelector, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  353. {
  354. var index = -1;
  355. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  356. {
  357. checked
  358. {
  359. index++;
  360. }
  361. var inner = await collectionSelector(element, index, cancellationToken).ConfigureAwait(false);
  362. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  363. {
  364. yield return await resultSelector(element, subElement, cancellationToken).ConfigureAwait(false);
  365. }
  366. }
  367. }
  368. }
  369. #endif
  370. private sealed class SelectManyAsyncIterator<TSource, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
  371. {
  372. private const int State_Source = 1;
  373. private const int State_Result = 2;
  374. private readonly Func<TSource, IAsyncEnumerable<TResult>> _selector;
  375. private readonly IAsyncEnumerable<TSource> _source;
  376. private int _mode;
  377. private IAsyncEnumerator<TResult>? _resultEnumerator;
  378. private IAsyncEnumerator<TSource>? _sourceEnumerator;
  379. public SelectManyAsyncIterator(IAsyncEnumerable<TSource> source, Func<TSource, IAsyncEnumerable<TResult>> selector)
  380. {
  381. _source = source;
  382. _selector = selector;
  383. }
  384. public override AsyncIteratorBase<TResult> Clone()
  385. {
  386. return new SelectManyAsyncIterator<TSource, TResult>(_source, _selector);
  387. }
  388. public override async ValueTask DisposeAsync()
  389. {
  390. if (_resultEnumerator != null)
  391. {
  392. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  393. _resultEnumerator = null;
  394. }
  395. if (_sourceEnumerator != null)
  396. {
  397. await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
  398. _sourceEnumerator = null;
  399. }
  400. await base.DisposeAsync().ConfigureAwait(false);
  401. }
  402. public ValueTask<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  403. {
  404. if (onlyIfCheap)
  405. {
  406. return new ValueTask<int>(-1);
  407. }
  408. return Core(cancellationToken);
  409. async ValueTask<int> Core(CancellationToken cancellationToken)
  410. {
  411. var count = 0;
  412. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  413. {
  414. checked
  415. {
  416. count += await _selector(element).CountAsync().ConfigureAwait(false);
  417. }
  418. }
  419. return count;
  420. }
  421. }
  422. public async ValueTask<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
  423. {
  424. // REVIEW: Substitute for SparseArrayBuilder<T> logic once we have access to that.
  425. var list = await ToListAsync(cancellationToken).ConfigureAwait(false);
  426. return list.ToArray();
  427. }
  428. public async ValueTask<List<TResult>> ToListAsync(CancellationToken cancellationToken)
  429. {
  430. var list = new List<TResult>();
  431. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  432. {
  433. var items = _selector(element);
  434. await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false);
  435. }
  436. return list;
  437. }
  438. protected override async ValueTask<bool> MoveNextCore()
  439. {
  440. switch (_state)
  441. {
  442. case AsyncIteratorState.Allocated:
  443. _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
  444. _mode = State_Source;
  445. _state = AsyncIteratorState.Iterating;
  446. goto case AsyncIteratorState.Iterating;
  447. case AsyncIteratorState.Iterating:
  448. switch (_mode)
  449. {
  450. case State_Source:
  451. if (await _sourceEnumerator!.MoveNextAsync().ConfigureAwait(false))
  452. {
  453. if (_resultEnumerator != null)
  454. {
  455. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  456. }
  457. var inner = _selector(_sourceEnumerator.Current);
  458. _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
  459. _mode = State_Result;
  460. goto case State_Result;
  461. }
  462. break;
  463. case State_Result:
  464. if (await _resultEnumerator!.MoveNextAsync().ConfigureAwait(false))
  465. {
  466. _current = _resultEnumerator.Current;
  467. return true;
  468. }
  469. _mode = State_Source;
  470. goto case State_Source; // loop
  471. }
  472. break;
  473. }
  474. await DisposeAsync().ConfigureAwait(false);
  475. return false;
  476. }
  477. }
  478. private sealed class SelectManyAsyncIteratorWithTask<TSource, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
  479. {
  480. private const int State_Source = 1;
  481. private const int State_Result = 2;
  482. private readonly Func<TSource, ValueTask<IAsyncEnumerable<TResult>>> _selector;
  483. private readonly IAsyncEnumerable<TSource> _source;
  484. private int _mode;
  485. private IAsyncEnumerator<TResult>? _resultEnumerator;
  486. private IAsyncEnumerator<TSource>? _sourceEnumerator;
  487. public SelectManyAsyncIteratorWithTask(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<IAsyncEnumerable<TResult>>> selector)
  488. {
  489. _source = source;
  490. _selector = selector;
  491. }
  492. public override AsyncIteratorBase<TResult> Clone()
  493. {
  494. return new SelectManyAsyncIteratorWithTask<TSource, TResult>(_source, _selector);
  495. }
  496. public override async ValueTask DisposeAsync()
  497. {
  498. if (_resultEnumerator != null)
  499. {
  500. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  501. _resultEnumerator = null;
  502. }
  503. if (_sourceEnumerator != null)
  504. {
  505. await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
  506. _sourceEnumerator = null;
  507. }
  508. await base.DisposeAsync().ConfigureAwait(false);
  509. }
  510. public ValueTask<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  511. {
  512. if (onlyIfCheap)
  513. {
  514. return new ValueTask<int>(-1);
  515. }
  516. return Core(cancellationToken);
  517. async ValueTask<int> Core(CancellationToken cancellationToken)
  518. {
  519. var count = 0;
  520. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  521. {
  522. var items = await _selector(element).ConfigureAwait(false);
  523. checked
  524. {
  525. count += await items.CountAsync().ConfigureAwait(false);
  526. }
  527. }
  528. return count;
  529. }
  530. }
  531. public async ValueTask<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
  532. {
  533. // REVIEW: Substitute for SparseArrayBuilder<T> logic once we have access to that.
  534. var list = await ToListAsync(cancellationToken).ConfigureAwait(false);
  535. return list.ToArray();
  536. }
  537. public async ValueTask<List<TResult>> ToListAsync(CancellationToken cancellationToken)
  538. {
  539. var list = new List<TResult>();
  540. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  541. {
  542. var items = await _selector(element).ConfigureAwait(false);
  543. await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false);
  544. }
  545. return list;
  546. }
  547. protected override async ValueTask<bool> MoveNextCore()
  548. {
  549. switch (_state)
  550. {
  551. case AsyncIteratorState.Allocated:
  552. _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
  553. _mode = State_Source;
  554. _state = AsyncIteratorState.Iterating;
  555. goto case AsyncIteratorState.Iterating;
  556. case AsyncIteratorState.Iterating:
  557. switch (_mode)
  558. {
  559. case State_Source:
  560. if (await _sourceEnumerator!.MoveNextAsync().ConfigureAwait(false))
  561. {
  562. if (_resultEnumerator != null)
  563. {
  564. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  565. }
  566. var inner = await _selector(_sourceEnumerator.Current).ConfigureAwait(false);
  567. _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
  568. _mode = State_Result;
  569. goto case State_Result;
  570. }
  571. break;
  572. case State_Result:
  573. if (await _resultEnumerator!.MoveNextAsync().ConfigureAwait(false))
  574. {
  575. _current = _resultEnumerator.Current;
  576. return true;
  577. }
  578. _mode = State_Source;
  579. goto case State_Source; // loop
  580. }
  581. break;
  582. }
  583. await DisposeAsync().ConfigureAwait(false);
  584. return false;
  585. }
  586. }
  587. #if !NO_DEEP_CANCELLATION
  588. private sealed class SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
  589. {
  590. private const int State_Source = 1;
  591. private const int State_Result = 2;
  592. private readonly Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> _selector;
  593. private readonly IAsyncEnumerable<TSource> _source;
  594. private int _mode;
  595. private IAsyncEnumerator<TResult>? _resultEnumerator;
  596. private IAsyncEnumerator<TSource>? _sourceEnumerator;
  597. public SelectManyAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
  598. {
  599. _source = source;
  600. _selector = selector;
  601. }
  602. public override AsyncIteratorBase<TResult> Clone()
  603. {
  604. return new SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult>(_source, _selector);
  605. }
  606. public override async ValueTask DisposeAsync()
  607. {
  608. if (_resultEnumerator != null)
  609. {
  610. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  611. _resultEnumerator = null;
  612. }
  613. if (_sourceEnumerator != null)
  614. {
  615. await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
  616. _sourceEnumerator = null;
  617. }
  618. await base.DisposeAsync().ConfigureAwait(false);
  619. }
  620. public ValueTask<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  621. {
  622. if (onlyIfCheap)
  623. {
  624. return new ValueTask<int>(-1);
  625. }
  626. return Core(cancellationToken);
  627. async ValueTask<int> Core(CancellationToken cancellationToken)
  628. {
  629. var count = 0;
  630. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  631. {
  632. var items = await _selector(element, cancellationToken).ConfigureAwait(false);
  633. checked
  634. {
  635. count += await items.CountAsync().ConfigureAwait(false);
  636. }
  637. }
  638. return count;
  639. }
  640. }
  641. public async ValueTask<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
  642. {
  643. // REVIEW: Substitute for SparseArrayBuilder<T> logic once we have access to that.
  644. var list = await ToListAsync(cancellationToken).ConfigureAwait(false);
  645. return list.ToArray();
  646. }
  647. public async ValueTask<List<TResult>> ToListAsync(CancellationToken cancellationToken)
  648. {
  649. var list = new List<TResult>();
  650. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  651. {
  652. var items = await _selector(element, cancellationToken).ConfigureAwait(false);
  653. await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false);
  654. }
  655. return list;
  656. }
  657. protected override async ValueTask<bool> MoveNextCore()
  658. {
  659. switch (_state)
  660. {
  661. case AsyncIteratorState.Allocated:
  662. _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
  663. _mode = State_Source;
  664. _state = AsyncIteratorState.Iterating;
  665. goto case AsyncIteratorState.Iterating;
  666. case AsyncIteratorState.Iterating:
  667. switch (_mode)
  668. {
  669. case State_Source:
  670. if (await _sourceEnumerator!.MoveNextAsync().ConfigureAwait(false))
  671. {
  672. if (_resultEnumerator != null)
  673. {
  674. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  675. }
  676. var inner = await _selector(_sourceEnumerator.Current, _cancellationToken).ConfigureAwait(false);
  677. _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
  678. _mode = State_Result;
  679. goto case State_Result;
  680. }
  681. break;
  682. case State_Result:
  683. if (await _resultEnumerator!.MoveNextAsync().ConfigureAwait(false))
  684. {
  685. _current = _resultEnumerator.Current;
  686. return true;
  687. }
  688. _mode = State_Source;
  689. goto case State_Source; // loop
  690. }
  691. break;
  692. }
  693. await DisposeAsync().ConfigureAwait(false);
  694. return false;
  695. }
  696. }
  697. #endif
  698. }
  699. }