ForEach.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. #if !HAS_AWAIT_FOREACH
  5. using System.Collections.Generic;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. namespace System.Linq
  9. {
  10. #if REFERENCE_ASSEMBLY
  11. public static partial class AsyncEnumerableDeprecated
  12. #else
  13. public static partial class AsyncEnumerable
  14. #endif
  15. {
  16. // REVIEW: Once we have C# 8.0 language support, we may want to do away with these methods. An open question is how to
  17. // provide support for cancellation, which could be offered through WithCancellation on the source. If we still
  18. // want to keep these methods, they may be a candidate for System.Interactive.Async if we consider them to be
  19. // non-standard (i.e. IEnumerable<T> doesn't have a ForEach extension method either).
  20. /// <summary>
  21. /// Invokes an action for each element in the async-enumerable sequence, and returns a Task object that will get signaled when the sequence terminates.
  22. /// </summary>
  23. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  24. /// <param name="source">Source sequence.</param>
  25. /// <param name="action">Action to invoke for each element in the async-enumerable sequence.</param>
  26. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  27. /// <returns>Task that signals the termination of the sequence.</returns>
  28. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="action"/> is null.</exception>
  29. /// <remarks>This operator is especially useful in conjunction with the asynchronous programming features introduced in C# 5.0 and Visual Basic 11.</remarks>
  30. [Obsolete("Use the language support for async foreach instead.")]
  31. public static Task ForEachAsync<TSource>(this IAsyncEnumerable<TSource> source, Action<TSource> action, CancellationToken cancellationToken = default)
  32. {
  33. if (source == null)
  34. throw Error.ArgumentNull(nameof(source));
  35. if (action == null)
  36. throw Error.ArgumentNull(nameof(action));
  37. return Core(source, action, cancellationToken);
  38. static async Task Core(IAsyncEnumerable<TSource> source, Action<TSource> action, CancellationToken cancellationToken)
  39. {
  40. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  41. {
  42. action(item);
  43. }
  44. }
  45. }
  46. /// <summary>
  47. /// Invokes an action for each element in the async-enumerable sequence, incorporating the element's index, and returns a Task object that will get signaled when the sequence terminates.
  48. /// </summary>
  49. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  50. /// <param name="source">Source sequence.</param>
  51. /// <param name="action">Action to invoke for each element in the async-enumerable sequence.</param>
  52. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  53. /// <returns>Task that signals the termination of the sequence.</returns>
  54. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="action"/> is null.</exception>
  55. /// <remarks>This operator is especially useful in conjunction with the asynchronous programming features introduced in C# 5.0 and Visual Basic 11.</remarks>
  56. [Obsolete("Use the language support for async foreach instead.")]
  57. public static Task ForEachAsync<TSource>(this IAsyncEnumerable<TSource> source, Action<TSource, int> action, CancellationToken cancellationToken = default)
  58. {
  59. if (source == null)
  60. throw Error.ArgumentNull(nameof(source));
  61. if (action == null)
  62. throw Error.ArgumentNull(nameof(action));
  63. return Core(source, action, cancellationToken);
  64. static async Task Core(IAsyncEnumerable<TSource> source, Action<TSource, int> action, CancellationToken cancellationToken)
  65. {
  66. var index = 0;
  67. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  68. {
  69. action(item, checked(index++));
  70. }
  71. }
  72. }
  73. /// <summary>
  74. /// Invokes and awaits an asynchronous action on each element in the source sequence, and returns a task that is signaled when the sequence terminates.
  75. /// </summary>
  76. /// <typeparam name="TSource">Type of elements in the sequence.</typeparam>
  77. /// <param name="source">Source sequence.</param>
  78. /// <param name="action">Asynchronous action to invoke and await for each element in the source sequence.</param>
  79. /// <param name="cancellationToken">Optional cancellation token for cancelling the sequence at any time.</param>
  80. /// <returns>Task that signals the termination of the sequence.</returns>
  81. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="action"/> is <see langword="null"/>.</exception>
  82. [GenerateAsyncOverload]
  83. [Obsolete("Use the language support for async foreach instead.")]
  84. private static Task ForEachAwaitAsyncCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, Task> action, CancellationToken cancellationToken = default)
  85. {
  86. if (source == null)
  87. throw Error.ArgumentNull(nameof(source));
  88. if (action == null)
  89. throw Error.ArgumentNull(nameof(action));
  90. return Core(source, action, cancellationToken);
  91. static async Task Core(IAsyncEnumerable<TSource> source, Func<TSource, Task> action, CancellationToken cancellationToken)
  92. {
  93. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  94. {
  95. await action(item).ConfigureAwait(false);
  96. }
  97. }
  98. }
  99. #if !NO_DEEP_CANCELLATION
  100. [GenerateAsyncOverload]
  101. [Obsolete("Use the language support for async foreach instead.")]
  102. internal static Task ForEachAwaitWithCancellationAsyncCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, Task> action, CancellationToken cancellationToken)
  103. {
  104. if (source == null)
  105. throw Error.ArgumentNull(nameof(source));
  106. if (action == null)
  107. throw Error.ArgumentNull(nameof(action));
  108. return Core(source, action, cancellationToken);
  109. static async Task Core(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, Task> action, CancellationToken cancellationToken)
  110. {
  111. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  112. {
  113. await action(item, cancellationToken).ConfigureAwait(false);
  114. }
  115. }
  116. }
  117. #endif
  118. /// <summary>
  119. /// Invokes and awaits an asynchronous action on each element in the source sequence, incorporating the element's index, and returns a task that is signaled when the sequence terminates.
  120. /// </summary>
  121. /// <typeparam name="TSource">Type of elements in the sequence.</typeparam>
  122. /// <param name="source">Source sequence.</param>
  123. /// <param name="action">Asynchronous action to invoke and await for each element in the source sequence; the second parameter represents the index of the element.</param>
  124. /// <param name="cancellationToken">Optional cancellation token for cancelling the sequence at any time.</param>
  125. /// <returns>Task that signals the termination of the sequence.</returns>
  126. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="action"/> is <see langword="null"/>.</exception>
  127. [GenerateAsyncOverload]
  128. [Obsolete("Use the language support for async foreach instead.")]
  129. private static Task ForEachAwaitAsyncCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, Task> action, CancellationToken cancellationToken = default)
  130. {
  131. if (source == null)
  132. throw Error.ArgumentNull(nameof(source));
  133. if (action == null)
  134. throw Error.ArgumentNull(nameof(action));
  135. return Core(source, action, cancellationToken);
  136. static async Task Core(IAsyncEnumerable<TSource> source, Func<TSource, int, Task> action, CancellationToken cancellationToken)
  137. {
  138. var index = 0;
  139. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  140. {
  141. await action(item, checked(index++)).ConfigureAwait(false);
  142. }
  143. }
  144. }
  145. #if !NO_DEEP_CANCELLATION
  146. [GenerateAsyncOverload]
  147. [Obsolete("Use the language support for async foreach instead.")]
  148. internal static Task ForEachAwaitWithCancellationAsyncCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, Task> action, CancellationToken cancellationToken)
  149. {
  150. if (source == null)
  151. throw Error.ArgumentNull(nameof(source));
  152. if (action == null)
  153. throw Error.ArgumentNull(nameof(action));
  154. return Core(source, action, cancellationToken);
  155. static async Task Core(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, Task> action, CancellationToken cancellationToken)
  156. {
  157. var index = 0;
  158. await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  159. {
  160. await action(item, checked(index++), cancellationToken).ConfigureAwait(false);
  161. }
  162. }
  163. }
  164. #endif
  165. }
  166. }
  167. #endif