AsyncEnumerableExtensions.cs 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  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.Runtime.CompilerServices;
  6. namespace System.Threading.Tasks
  7. {
  8. public static class AsyncEnumerableExtensions
  9. {
  10. public static ConfiguredAsyncEnumerable<T> ConfigureAwait<T>(this IAsyncEnumerable<T> enumerable, bool continueOnCapturedContext)
  11. {
  12. if (enumerable == null)
  13. throw Error.ArgumentNull(nameof(enumerable));
  14. return new ConfiguredAsyncEnumerable<T>(enumerable, continueOnCapturedContext);
  15. }
  16. public static ConfiguredAsyncEnumerable<T>.ConfiguredAsyncEnumerator ConfigureAwait<T>(this IAsyncEnumerator<T> enumerator, bool continueOnCapturedContext)
  17. {
  18. if (enumerator == null)
  19. throw Error.ArgumentNull(nameof(enumerator));
  20. return new ConfiguredAsyncEnumerable<T>.ConfiguredAsyncEnumerator(enumerator, continueOnCapturedContext);
  21. }
  22. // REVIEW: Explicit implementation of the interfaces allows for composition with other "modifier operators" such as WithCancellation.
  23. // We expect that the "await foreach" statement will bind to the public struct methods, thus avoiding boxing.
  24. public readonly struct ConfiguredAsyncEnumerable<T> : IAsyncEnumerable<T>
  25. {
  26. private readonly IAsyncEnumerable<T> _enumerable;
  27. private readonly bool _continueOnCapturedContext;
  28. internal ConfiguredAsyncEnumerable(IAsyncEnumerable<T> enumerable, bool continueOnCapturedContext)
  29. {
  30. _enumerable = enumerable;
  31. _continueOnCapturedContext = continueOnCapturedContext;
  32. }
  33. public ConfiguredAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken)
  34. {
  35. cancellationToken.ThrowIfCancellationRequested(); // NB: [LDM-2018-11-28] Equivalent to async iterator behavior.
  36. return new ConfiguredAsyncEnumerator(_enumerable.GetAsyncEnumerator(cancellationToken), _continueOnCapturedContext);
  37. }
  38. IAsyncEnumerator<T> IAsyncEnumerable<T>.GetAsyncEnumerator(CancellationToken cancellationToken) =>
  39. GetAsyncEnumerator(cancellationToken);
  40. public readonly struct ConfiguredAsyncEnumerator : IAsyncEnumerator<T>
  41. {
  42. private readonly IAsyncEnumerator<T> _enumerator;
  43. private readonly bool _continueOnCapturedContext;
  44. internal ConfiguredAsyncEnumerator(IAsyncEnumerator<T> enumerator, bool continueOnCapturedContext)
  45. {
  46. _enumerator = enumerator;
  47. _continueOnCapturedContext = continueOnCapturedContext;
  48. }
  49. public ConfiguredValueTaskAwaitable<bool> MoveNextAsync() =>
  50. _enumerator.MoveNextAsync().ConfigureAwait(_continueOnCapturedContext);
  51. public T Current => _enumerator.Current;
  52. public ConfiguredValueTaskAwaitable DisposeAsync() =>
  53. _enumerator.DisposeAsync().ConfigureAwait(_continueOnCapturedContext);
  54. async ValueTask<bool> IAsyncEnumerator<T>.MoveNextAsync() =>
  55. await _enumerator.MoveNextAsync().ConfigureAwait(_continueOnCapturedContext);
  56. async ValueTask IAsyncDisposable.DisposeAsync() =>
  57. await _enumerator.DisposeAsync().ConfigureAwait(_continueOnCapturedContext);
  58. }
  59. }
  60. }
  61. }