SelectMany.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. namespace System.Linq
  9. {
  10. public static partial class AsyncEnumerable
  11. {
  12. public static IAsyncEnumerable<TResult> SelectMany<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, IAsyncEnumerable<TResult>> selector)
  13. {
  14. if (source == null)
  15. throw Error.ArgumentNull(nameof(source));
  16. if (selector == null)
  17. throw Error.ArgumentNull(nameof(selector));
  18. return new SelectManyAsyncIterator<TSource, TResult>(source, selector);
  19. }
  20. // REVIEW: Should we keep these overloads that return ValueTask<IAsyncEnumerable<TResult>>? One could argue the selector is async twice.
  21. internal static IAsyncEnumerable<TResult> SelectManyAwaitCore<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<IAsyncEnumerable<TResult>>> selector)
  22. {
  23. if (source == null)
  24. throw Error.ArgumentNull(nameof(source));
  25. if (selector == null)
  26. throw Error.ArgumentNull(nameof(selector));
  27. return new SelectManyAsyncIteratorWithTask<TSource, TResult>(source, selector);
  28. }
  29. #if !NO_DEEP_CANCELLATION
  30. internal static IAsyncEnumerable<TResult> SelectManyAwaitWithCancellationCore<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
  31. {
  32. if (source == null)
  33. throw Error.ArgumentNull(nameof(source));
  34. if (selector == null)
  35. throw Error.ArgumentNull(nameof(selector));
  36. return new SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult>(source, selector);
  37. }
  38. #endif
  39. public static IAsyncEnumerable<TResult> SelectMany<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, IAsyncEnumerable<TResult>> selector)
  40. {
  41. if (source == null)
  42. throw Error.ArgumentNull(nameof(source));
  43. if (selector == null)
  44. throw Error.ArgumentNull(nameof(selector));
  45. return Create(Core);
  46. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  47. {
  48. var index = -1;
  49. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  50. {
  51. checked
  52. {
  53. index++;
  54. }
  55. var inner = selector(element, index);
  56. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  57. {
  58. yield return subElement;
  59. }
  60. }
  61. }
  62. }
  63. internal static IAsyncEnumerable<TResult> SelectManyAwaitCore<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<IAsyncEnumerable<TResult>>> selector)
  64. {
  65. if (source == null)
  66. throw Error.ArgumentNull(nameof(source));
  67. if (selector == null)
  68. throw Error.ArgumentNull(nameof(selector));
  69. return Create(Core);
  70. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  71. {
  72. var index = -1;
  73. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  74. {
  75. checked
  76. {
  77. index++;
  78. }
  79. var inner = await selector(element, index).ConfigureAwait(false);
  80. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  81. {
  82. yield return subElement;
  83. }
  84. }
  85. }
  86. }
  87. #if !NO_DEEP_CANCELLATION
  88. internal static IAsyncEnumerable<TResult> SelectManyAwaitWithCancellationCore<TSource, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
  89. {
  90. if (source == null)
  91. throw Error.ArgumentNull(nameof(source));
  92. if (selector == null)
  93. throw Error.ArgumentNull(nameof(selector));
  94. return Create(Core);
  95. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  96. {
  97. var index = -1;
  98. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  99. {
  100. checked
  101. {
  102. index++;
  103. }
  104. var inner = await selector(element, index, cancellationToken).ConfigureAwait(false);
  105. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  106. {
  107. yield return subElement;
  108. }
  109. }
  110. }
  111. }
  112. #endif
  113. public static IAsyncEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, IAsyncEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
  114. {
  115. if (source == null)
  116. throw Error.ArgumentNull(nameof(source));
  117. if (collectionSelector == null)
  118. throw Error.ArgumentNull(nameof(collectionSelector));
  119. if (resultSelector == null)
  120. throw Error.ArgumentNull(nameof(resultSelector));
  121. return Create(Core);
  122. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  123. {
  124. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  125. {
  126. var inner = collectionSelector(element);
  127. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  128. {
  129. yield return resultSelector(element, subElement);
  130. }
  131. }
  132. }
  133. }
  134. internal static IAsyncEnumerable<TResult> SelectManyAwaitCore<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, ValueTask<TResult>> resultSelector)
  135. {
  136. if (source == null)
  137. throw Error.ArgumentNull(nameof(source));
  138. if (collectionSelector == null)
  139. throw Error.ArgumentNull(nameof(collectionSelector));
  140. if (resultSelector == null)
  141. throw Error.ArgumentNull(nameof(resultSelector));
  142. return Create(Core);
  143. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  144. {
  145. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  146. {
  147. var inner = await collectionSelector(element).ConfigureAwait(false);
  148. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  149. {
  150. yield return await resultSelector(element, subElement).ConfigureAwait(false);
  151. }
  152. }
  153. }
  154. }
  155. #if !NO_DEEP_CANCELLATION
  156. internal 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)
  157. {
  158. if (source == null)
  159. throw Error.ArgumentNull(nameof(source));
  160. if (collectionSelector == null)
  161. throw Error.ArgumentNull(nameof(collectionSelector));
  162. if (resultSelector == null)
  163. throw Error.ArgumentNull(nameof(resultSelector));
  164. return Create(Core);
  165. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  166. {
  167. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  168. {
  169. var inner = await collectionSelector(element, cancellationToken).ConfigureAwait(false);
  170. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  171. {
  172. yield return await resultSelector(element, subElement, cancellationToken).ConfigureAwait(false);
  173. }
  174. }
  175. }
  176. }
  177. #endif
  178. public static IAsyncEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, IAsyncEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)
  179. {
  180. if (source == null)
  181. throw Error.ArgumentNull(nameof(source));
  182. if (collectionSelector == null)
  183. throw Error.ArgumentNull(nameof(collectionSelector));
  184. if (resultSelector == null)
  185. throw Error.ArgumentNull(nameof(resultSelector));
  186. return Create(Core);
  187. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  188. {
  189. var index = -1;
  190. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  191. {
  192. checked
  193. {
  194. index++;
  195. }
  196. var inner = collectionSelector(element, index);
  197. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  198. {
  199. yield return resultSelector(element, subElement);
  200. }
  201. }
  202. }
  203. }
  204. internal static IAsyncEnumerable<TResult> SelectManyAwaitCore<TSource, TCollection, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<IAsyncEnumerable<TCollection>>> collectionSelector, Func<TSource, TCollection, ValueTask<TResult>> resultSelector)
  205. {
  206. if (source == null)
  207. throw Error.ArgumentNull(nameof(source));
  208. if (collectionSelector == null)
  209. throw Error.ArgumentNull(nameof(collectionSelector));
  210. if (resultSelector == null)
  211. throw Error.ArgumentNull(nameof(resultSelector));
  212. return Create(Core);
  213. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  214. {
  215. var index = -1;
  216. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  217. {
  218. checked
  219. {
  220. index++;
  221. }
  222. var inner = await collectionSelector(element, index).ConfigureAwait(false);
  223. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  224. {
  225. yield return await resultSelector(element, subElement).ConfigureAwait(false);
  226. }
  227. }
  228. }
  229. }
  230. #if !NO_DEEP_CANCELLATION
  231. internal 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)
  232. {
  233. if (source == null)
  234. throw Error.ArgumentNull(nameof(source));
  235. if (collectionSelector == null)
  236. throw Error.ArgumentNull(nameof(collectionSelector));
  237. if (resultSelector == null)
  238. throw Error.ArgumentNull(nameof(resultSelector));
  239. return Create(Core);
  240. async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
  241. {
  242. var index = -1;
  243. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  244. {
  245. checked
  246. {
  247. index++;
  248. }
  249. var inner = await collectionSelector(element, index, cancellationToken).ConfigureAwait(false);
  250. await foreach (var subElement in inner.WithCancellation(cancellationToken).ConfigureAwait(false))
  251. {
  252. yield return await resultSelector(element, subElement, cancellationToken).ConfigureAwait(false);
  253. }
  254. }
  255. }
  256. }
  257. #endif
  258. private sealed class SelectManyAsyncIterator<TSource, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
  259. {
  260. private const int State_Source = 1;
  261. private const int State_Result = 2;
  262. private readonly Func<TSource, IAsyncEnumerable<TResult>> _selector;
  263. private readonly IAsyncEnumerable<TSource> _source;
  264. private int _mode;
  265. private IAsyncEnumerator<TResult>? _resultEnumerator;
  266. private IAsyncEnumerator<TSource>? _sourceEnumerator;
  267. public SelectManyAsyncIterator(IAsyncEnumerable<TSource> source, Func<TSource, IAsyncEnumerable<TResult>> selector)
  268. {
  269. Debug.Assert(source != null);
  270. Debug.Assert(selector != null);
  271. _source = source;
  272. _selector = selector;
  273. }
  274. public override AsyncIteratorBase<TResult> Clone()
  275. {
  276. return new SelectManyAsyncIterator<TSource, TResult>(_source, _selector);
  277. }
  278. public override async ValueTask DisposeAsync()
  279. {
  280. if (_resultEnumerator != null)
  281. {
  282. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  283. _resultEnumerator = null;
  284. }
  285. if (_sourceEnumerator != null)
  286. {
  287. await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
  288. _sourceEnumerator = null;
  289. }
  290. await base.DisposeAsync().ConfigureAwait(false);
  291. }
  292. public ValueTask<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  293. {
  294. if (onlyIfCheap)
  295. {
  296. return new ValueTask<int>(-1);
  297. }
  298. return Core(cancellationToken);
  299. async ValueTask<int> Core(CancellationToken _cancellationToken)
  300. {
  301. var count = 0;
  302. await foreach (var element in _source.WithCancellation(_cancellationToken).ConfigureAwait(false))
  303. {
  304. checked
  305. {
  306. count += await _selector(element).CountAsync().ConfigureAwait(false);
  307. }
  308. }
  309. return count;
  310. }
  311. }
  312. public async ValueTask<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
  313. {
  314. // REVIEW: Substitute for SparseArrayBuilder<T> logic once we have access to that.
  315. var list = await ToListAsync(cancellationToken).ConfigureAwait(false);
  316. return list.ToArray();
  317. }
  318. public async ValueTask<List<TResult>> ToListAsync(CancellationToken cancellationToken)
  319. {
  320. var list = new List<TResult>();
  321. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  322. {
  323. var items = _selector(element);
  324. await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false);
  325. }
  326. return list;
  327. }
  328. protected override async ValueTask<bool> MoveNextCore()
  329. {
  330. switch (_state)
  331. {
  332. case AsyncIteratorState.Allocated:
  333. _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
  334. _mode = State_Source;
  335. _state = AsyncIteratorState.Iterating;
  336. goto case AsyncIteratorState.Iterating;
  337. case AsyncIteratorState.Iterating:
  338. switch (_mode)
  339. {
  340. case State_Source:
  341. if (await _sourceEnumerator!.MoveNextAsync().ConfigureAwait(false))
  342. {
  343. if (_resultEnumerator != null)
  344. {
  345. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  346. }
  347. var inner = _selector(_sourceEnumerator.Current);
  348. _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
  349. _mode = State_Result;
  350. goto case State_Result;
  351. }
  352. break;
  353. case State_Result:
  354. if (await _resultEnumerator!.MoveNextAsync().ConfigureAwait(false))
  355. {
  356. _current = _resultEnumerator.Current;
  357. return true;
  358. }
  359. _mode = State_Source;
  360. goto case State_Source; // loop
  361. }
  362. break;
  363. }
  364. await DisposeAsync().ConfigureAwait(false);
  365. return false;
  366. }
  367. }
  368. private sealed class SelectManyAsyncIteratorWithTask<TSource, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
  369. {
  370. private const int State_Source = 1;
  371. private const int State_Result = 2;
  372. private readonly Func<TSource, ValueTask<IAsyncEnumerable<TResult>>> _selector;
  373. private readonly IAsyncEnumerable<TSource> _source;
  374. private int _mode;
  375. private IAsyncEnumerator<TResult>? _resultEnumerator;
  376. private IAsyncEnumerator<TSource>? _sourceEnumerator;
  377. public SelectManyAsyncIteratorWithTask(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<IAsyncEnumerable<TResult>>> selector)
  378. {
  379. Debug.Assert(source != null);
  380. Debug.Assert(selector != null);
  381. _source = source;
  382. _selector = selector;
  383. }
  384. public override AsyncIteratorBase<TResult> Clone()
  385. {
  386. return new SelectManyAsyncIteratorWithTask<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. var items = await _selector(element).ConfigureAwait(false);
  415. checked
  416. {
  417. count += await items.CountAsync().ConfigureAwait(false);
  418. }
  419. }
  420. return count;
  421. }
  422. }
  423. public async ValueTask<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
  424. {
  425. // REVIEW: Substitute for SparseArrayBuilder<T> logic once we have access to that.
  426. var list = await ToListAsync(cancellationToken).ConfigureAwait(false);
  427. return list.ToArray();
  428. }
  429. public async ValueTask<List<TResult>> ToListAsync(CancellationToken cancellationToken)
  430. {
  431. var list = new List<TResult>();
  432. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  433. {
  434. var items = await _selector(element).ConfigureAwait(false);
  435. await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false);
  436. }
  437. return list;
  438. }
  439. protected override async ValueTask<bool> MoveNextCore()
  440. {
  441. switch (_state)
  442. {
  443. case AsyncIteratorState.Allocated:
  444. _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
  445. _mode = State_Source;
  446. _state = AsyncIteratorState.Iterating;
  447. goto case AsyncIteratorState.Iterating;
  448. case AsyncIteratorState.Iterating:
  449. switch (_mode)
  450. {
  451. case State_Source:
  452. if (await _sourceEnumerator!.MoveNextAsync().ConfigureAwait(false))
  453. {
  454. if (_resultEnumerator != null)
  455. {
  456. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  457. }
  458. var inner = await _selector(_sourceEnumerator.Current).ConfigureAwait(false);
  459. _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
  460. _mode = State_Result;
  461. goto case State_Result;
  462. }
  463. break;
  464. case State_Result:
  465. if (await _resultEnumerator!.MoveNextAsync().ConfigureAwait(false))
  466. {
  467. _current = _resultEnumerator.Current;
  468. return true;
  469. }
  470. _mode = State_Source;
  471. goto case State_Source; // loop
  472. }
  473. break;
  474. }
  475. await DisposeAsync().ConfigureAwait(false);
  476. return false;
  477. }
  478. }
  479. #if !NO_DEEP_CANCELLATION
  480. private sealed class SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
  481. {
  482. private const int State_Source = 1;
  483. private const int State_Result = 2;
  484. private readonly Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> _selector;
  485. private readonly IAsyncEnumerable<TSource> _source;
  486. private int _mode;
  487. private IAsyncEnumerator<TResult>? _resultEnumerator;
  488. private IAsyncEnumerator<TSource>? _sourceEnumerator;
  489. public SelectManyAsyncIteratorWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<IAsyncEnumerable<TResult>>> selector)
  490. {
  491. Debug.Assert(source != null);
  492. Debug.Assert(selector != null);
  493. _source = source;
  494. _selector = selector;
  495. }
  496. public override AsyncIteratorBase<TResult> Clone()
  497. {
  498. return new SelectManyAsyncIteratorWithTaskAndCancellation<TSource, TResult>(_source, _selector);
  499. }
  500. public override async ValueTask DisposeAsync()
  501. {
  502. if (_resultEnumerator != null)
  503. {
  504. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  505. _resultEnumerator = null;
  506. }
  507. if (_sourceEnumerator != null)
  508. {
  509. await _sourceEnumerator.DisposeAsync().ConfigureAwait(false);
  510. _sourceEnumerator = null;
  511. }
  512. await base.DisposeAsync().ConfigureAwait(false);
  513. }
  514. public ValueTask<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  515. {
  516. if (onlyIfCheap)
  517. {
  518. return new ValueTask<int>(-1);
  519. }
  520. return Core(cancellationToken);
  521. async ValueTask<int> Core(CancellationToken _cancellationToken)
  522. {
  523. var count = 0;
  524. await foreach (var element in _source.WithCancellation(_cancellationToken).ConfigureAwait(false))
  525. {
  526. var items = await _selector(element, _cancellationToken).ConfigureAwait(false);
  527. checked
  528. {
  529. count += await items.CountAsync().ConfigureAwait(false);
  530. }
  531. }
  532. return count;
  533. }
  534. }
  535. public async ValueTask<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
  536. {
  537. // REVIEW: Substitute for SparseArrayBuilder<T> logic once we have access to that.
  538. var list = await ToListAsync(cancellationToken).ConfigureAwait(false);
  539. return list.ToArray();
  540. }
  541. public async ValueTask<List<TResult>> ToListAsync(CancellationToken cancellationToken)
  542. {
  543. var list = new List<TResult>();
  544. await foreach (var element in _source.WithCancellation(cancellationToken).ConfigureAwait(false))
  545. {
  546. var items = await _selector(element, cancellationToken).ConfigureAwait(false);
  547. await list.AddRangeAsync(items, cancellationToken).ConfigureAwait(false);
  548. }
  549. return list;
  550. }
  551. protected override async ValueTask<bool> MoveNextCore()
  552. {
  553. switch (_state)
  554. {
  555. case AsyncIteratorState.Allocated:
  556. _sourceEnumerator = _source.GetAsyncEnumerator(_cancellationToken);
  557. _mode = State_Source;
  558. _state = AsyncIteratorState.Iterating;
  559. goto case AsyncIteratorState.Iterating;
  560. case AsyncIteratorState.Iterating:
  561. switch (_mode)
  562. {
  563. case State_Source:
  564. if (await _sourceEnumerator!.MoveNextAsync().ConfigureAwait(false))
  565. {
  566. if (_resultEnumerator != null)
  567. {
  568. await _resultEnumerator.DisposeAsync().ConfigureAwait(false);
  569. }
  570. var inner = await _selector(_sourceEnumerator.Current, _cancellationToken).ConfigureAwait(false);
  571. _resultEnumerator = inner.GetAsyncEnumerator(_cancellationToken);
  572. _mode = State_Result;
  573. goto case State_Result;
  574. }
  575. break;
  576. case State_Result:
  577. if (await _resultEnumerator!.MoveNextAsync().ConfigureAwait(false))
  578. {
  579. _current = _resultEnumerator.Current;
  580. return true;
  581. }
  582. _mode = State_Source;
  583. goto case State_Source; // loop
  584. }
  585. break;
  586. }
  587. await DisposeAsync().ConfigureAwait(false);
  588. return false;
  589. }
  590. }
  591. #endif
  592. }
  593. }