TakeWhile.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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.takewhile?view=net-9.0-pp#system-linq-asyncenumerable-takewhile-1(system-collections-generic-iasyncenumerable((-0))-system-func((-0-system-boolean)))
  17. /// <summary>
  18. /// Returns elements from an async-enumerable sequence as long as a specified condition is true.
  19. /// </summary>
  20. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  21. /// <param name="source">A sequence to return elements from.</param>
  22. /// <param name="predicate">A function to test each element for a condition.</param>
  23. /// <returns>An async-enumerable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes.</returns>
  24. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="predicate"/> is null.</exception>
  25. public static IAsyncEnumerable<TSource> TakeWhile<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, bool> predicate)
  26. {
  27. if (source == null)
  28. throw Error.ArgumentNull(nameof(source));
  29. if (predicate == null)
  30. throw Error.ArgumentNull(nameof(predicate));
  31. return Core(source, predicate);
  32. static async IAsyncEnumerable<TSource> Core(IAsyncEnumerable<TSource> source, Func<TSource, bool> predicate, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  33. {
  34. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  35. {
  36. if (!predicate(element))
  37. {
  38. break;
  39. }
  40. yield return element;
  41. }
  42. }
  43. }
  44. // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.takewhile?view=net-9.0-pp#system-linq-asyncenumerable-takewhile-1(system-collections-generic-iasyncenumerable((-0))-system-func((-0-system-int32-system-boolean)))
  45. /// <summary>
  46. /// Returns elements from an async-enumerable sequence as long as a specified condition is true.
  47. /// The element's index is used in the logic of the predicate function.
  48. /// </summary>
  49. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  50. /// <param name="source">A sequence to return elements from.</param>
  51. /// <param name="predicate">A function to test each element for a condition; the second parameter of the function represents the index of the source element.</param>
  52. /// <returns>An async-enumerable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes.</returns>
  53. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="predicate"/> is null.</exception>
  54. public static IAsyncEnumerable<TSource> TakeWhile<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate)
  55. {
  56. if (source == null)
  57. throw Error.ArgumentNull(nameof(source));
  58. if (predicate == null)
  59. throw Error.ArgumentNull(nameof(predicate));
  60. return Core(source, predicate);
  61. static async IAsyncEnumerable<TSource> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  62. {
  63. var index = -1;
  64. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  65. {
  66. checked
  67. {
  68. index++;
  69. }
  70. if (!predicate(element, index))
  71. {
  72. break;
  73. }
  74. yield return element;
  75. }
  76. }
  77. }
  78. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  79. /// <summary>
  80. /// Returns elements from an async-enumerable sequence as long as a specified condition is true.
  81. /// </summary>
  82. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  83. /// <param name="source">A sequence to return elements from.</param>
  84. /// <param name="predicate">An asynchronous predicate to test each element for a condition.</param>
  85. /// <returns>An async-enumerable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes.</returns>
  86. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="predicate"/> is null.</exception>
  87. [GenerateAsyncOverload]
  88. [Obsolete("Use TakeWhile. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the TakeWhileAwait functionality now exists as overloads of TakeWhile. You will need to modify your callback to take an additional CancellationToken argument.")]
  89. private static IAsyncEnumerable<TSource> TakeWhileAwaitCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<bool>> predicate)
  90. {
  91. if (source == null)
  92. throw Error.ArgumentNull(nameof(source));
  93. if (predicate == null)
  94. throw Error.ArgumentNull(nameof(predicate));
  95. return Core(source, predicate);
  96. static async IAsyncEnumerable<TSource> Core(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<bool>> predicate, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  97. {
  98. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  99. {
  100. if (!await predicate(element).ConfigureAwait(false))
  101. {
  102. break;
  103. }
  104. yield return element;
  105. }
  106. }
  107. }
  108. #if !NO_DEEP_CANCELLATION
  109. [GenerateAsyncOverload]
  110. [Obsolete("Use TakeWhile. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the TakeWhileAwaitWithCancellation functionality now exists as overloads of TakeWhile.")]
  111. private static IAsyncEnumerable<TSource> TakeWhileAwaitWithCancellationCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate)
  112. {
  113. if (source == null)
  114. throw Error.ArgumentNull(nameof(source));
  115. if (predicate == null)
  116. throw Error.ArgumentNull(nameof(predicate));
  117. return Core(source, predicate);
  118. static async IAsyncEnumerable<TSource> Core(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  119. {
  120. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  121. {
  122. if (!await predicate(element, cancellationToken).ConfigureAwait(false))
  123. {
  124. break;
  125. }
  126. yield return element;
  127. }
  128. }
  129. }
  130. #endif
  131. /// <summary>
  132. /// Returns elements from an async-enumerable sequence as long as a specified condition is true.
  133. /// The element's index is used in the logic of the predicate function.
  134. /// </summary>
  135. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  136. /// <param name="source">A sequence to return elements from.</param>
  137. /// <param name="predicate">An asynchronous function to test each element for a condition; the second parameter of the function represents the index of the source element.</param>
  138. /// <returns>An async-enumerable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes.</returns>
  139. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="predicate"/> is null.</exception>
  140. [GenerateAsyncOverload]
  141. [Obsolete("Use TakeWhile. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the TakeWhileAwait functionality now exists as overloads of TakeWhile. You will need to modify your callback to take an additional CancellationToken argument.")]
  142. private static IAsyncEnumerable<TSource> TakeWhileAwaitCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<bool>> predicate)
  143. {
  144. if (source == null)
  145. throw Error.ArgumentNull(nameof(source));
  146. if (predicate == null)
  147. throw Error.ArgumentNull(nameof(predicate));
  148. return Core(source, predicate);
  149. static async IAsyncEnumerable<TSource> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, ValueTask<bool>> predicate, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  150. {
  151. var index = -1;
  152. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  153. {
  154. checked
  155. {
  156. index++;
  157. }
  158. if (!await predicate(element, index).ConfigureAwait(false))
  159. {
  160. break;
  161. }
  162. yield return element;
  163. }
  164. }
  165. }
  166. #if !NO_DEEP_CANCELLATION
  167. [GenerateAsyncOverload]
  168. [Obsolete("Use TakeWhile. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the TakeWhileAwaitWithCancellation functionality now exists as overloads of TakeWhile.")]
  169. private static IAsyncEnumerable<TSource> TakeWhileAwaitWithCancellationCore<TSource>(this IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<bool>> predicate)
  170. {
  171. if (source == null)
  172. throw Error.ArgumentNull(nameof(source));
  173. if (predicate == null)
  174. throw Error.ArgumentNull(nameof(predicate));
  175. return Core(source, predicate);
  176. static async IAsyncEnumerable<TSource> Core(IAsyncEnumerable<TSource> source, Func<TSource, int, CancellationToken, ValueTask<bool>> predicate, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
  177. {
  178. var index = -1;
  179. await foreach (var element in source.WithCancellation(cancellationToken).ConfigureAwait(false))
  180. {
  181. checked
  182. {
  183. index++;
  184. }
  185. if (!await predicate(element, index, cancellationToken).ConfigureAwait(false))
  186. {
  187. break;
  188. }
  189. yield return element;
  190. }
  191. }
  192. }
  193. #endif
  194. }
  195. }