ToLookup.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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.tolookupasync?view=net-9.0-pp#system-linq-asyncenumerable-tolookupasync-2(system-collections-generic-iasyncenumerable((-0))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))-system-threading-cancellationtoken)
  17. // That one overload covers the next two methods, because it supplies a default comparer.
  18. /// <summary>
  19. /// Creates a lookup from an async-enumerable sequence according to a specified key selector function.
  20. /// </summary>
  21. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  22. /// <typeparam name="TKey">The type of the lookup key computed for each element in the source sequence.</typeparam>
  23. /// <param name="source">An async-enumerable sequence to create a lookup for.</param>
  24. /// <param name="keySelector">A function to extract a key from each element.</param>
  25. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  26. /// <returns>An async-enumerable sequence containing a single element with a lookup mapping unique key values onto the corresponding source sequence's elements.</returns>
  27. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> is null.</exception>
  28. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  29. public static ValueTask<ILookup<TKey, TSource>> ToLookupAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, CancellationToken cancellationToken = default) =>
  30. ToLookupAsync(source, keySelector, comparer: null, cancellationToken);
  31. /// <summary>
  32. /// Creates a lookup from an async-enumerable sequence according to a specified key selector function, and a comparer.
  33. /// </summary>
  34. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  35. /// <typeparam name="TKey">The type of the lookup key computed for each element in the source sequence.</typeparam>
  36. /// <param name="source">An async-enumerable sequence to create a lookup for.</param>
  37. /// <param name="keySelector">A function to extract a key from each element.</param>
  38. /// <param name="comparer">An equality comparer to compare keys.</param>
  39. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  40. /// <returns>An async-enumerable sequence containing a single element with a lookup mapping unique key values onto the corresponding source sequence's elements.</returns>
  41. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="comparer"/> is null.</exception>
  42. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  43. public static ValueTask<ILookup<TKey, TSource>> ToLookupAsync<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default)
  44. {
  45. if (source == null)
  46. throw Error.ArgumentNull(nameof(source));
  47. if (keySelector == null)
  48. throw Error.ArgumentNull(nameof(keySelector));
  49. return Core(source, keySelector, comparer, cancellationToken);
  50. static async ValueTask<ILookup<TKey, TSource>> Core(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  51. {
  52. return await Internal.Lookup<TKey, TSource>.CreateAsync(source, keySelector, comparer, cancellationToken).ConfigureAwait(false);
  53. }
  54. }
  55. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  56. /// <summary>
  57. /// Creates a lookup from an async-enumerable sequence by invoking a key-selector function on each element and awaiting the result.
  58. /// </summary>
  59. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  60. /// <typeparam name="TKey">The type of the lookup key computed for each element in the source sequence.</typeparam>
  61. /// <param name="source">An async-enumerable sequence to create a lookup for.</param>
  62. /// <param name="keySelector">An asynchronous function to extract a key from each element.</param>
  63. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  64. /// <returns>A ValueTask containing a lookup mapping unique key values onto the corresponding source sequence's elements.</returns>
  65. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> is null.</exception>
  66. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  67. [GenerateAsyncOverload]
  68. [Obsolete("Use ToLookupAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToLookupAwaitAsync functionality now exists as overloads of ToLookupAsync.")]
  69. private static ValueTask<ILookup<TKey, TSource>> ToLookupAwaitAsyncCore<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, CancellationToken cancellationToken = default) =>
  70. ToLookupAwaitAsyncCore<TSource, TKey>(source, keySelector, comparer:null, cancellationToken);
  71. /// <summary>
  72. /// Creates a lookup from an async-enumerable sequence by invoking a key-selector function on each element and awaiting the result.
  73. /// </summary>
  74. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  75. /// <typeparam name="TKey">The type of the lookup key computed for each element in the source sequence.</typeparam>
  76. /// <param name="source">An async-enumerable sequence to create a lookup for.</param>
  77. /// <param name="keySelector">An asynchronous function to extract a key from each element.</param>
  78. /// <param name="comparer">An equality comparer to compare keys.</param>
  79. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  80. /// <returns>A ValueTask containing a lookup mapping unique key values onto the corresponding source sequence's elements.</returns>
  81. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="comparer"/> is null.</exception>
  82. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  83. [GenerateAsyncOverload]
  84. [Obsolete("Use ToLookupAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToLookupAwaitAsync functionality now exists as overloads of ToLookupAsync.")]
  85. private static ValueTask<ILookup<TKey, TSource>> ToLookupAwaitAsyncCore<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default)
  86. {
  87. if (source == null)
  88. throw Error.ArgumentNull(nameof(source));
  89. if (keySelector == null)
  90. throw Error.ArgumentNull(nameof(keySelector));
  91. return Core(source, keySelector, comparer, cancellationToken);
  92. static async ValueTask<ILookup<TKey, TSource>> Core(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  93. {
  94. return await Internal.LookupWithTask<TKey, TSource>.CreateAsync(source, keySelector, comparer, cancellationToken).ConfigureAwait(false);
  95. }
  96. }
  97. #if !NO_DEEP_CANCELLATION
  98. [GenerateAsyncOverload]
  99. [Obsolete("Use ToLookupAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToLookupAwaitWithCancellationAsync functionality now exists as overloads of ToLookupAsync.")]
  100. private static ValueTask<ILookup<TKey, TSource>> ToLookupAwaitWithCancellationAsyncCore<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, CancellationToken cancellationToken = default) =>
  101. ToLookupAwaitWithCancellationAsyncCore(source, keySelector, comparer: null, cancellationToken);
  102. [GenerateAsyncOverload]
  103. [Obsolete("Use ToLookupAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToLookupAwaitWithCancellationAsync functionality now exists as overloads of ToLookupAsync.")]
  104. private static ValueTask<ILookup<TKey, TSource>> ToLookupAwaitWithCancellationAsyncCore<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default)
  105. {
  106. if (source == null)
  107. throw Error.ArgumentNull(nameof(source));
  108. if (keySelector == null)
  109. throw Error.ArgumentNull(nameof(keySelector));
  110. return Core(source, keySelector, comparer, cancellationToken);
  111. static async ValueTask<ILookup<TKey, TSource>> Core(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  112. {
  113. return await Internal.LookupWithTask<TKey, TSource>.CreateAsync(source, keySelector, comparer, cancellationToken).ConfigureAwait(false);
  114. }
  115. }
  116. #endif
  117. #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  118. // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.tolookupasync?view=net-9.0-pp#system-linq-asyncenumerable-tolookupasync-3(system-collections-generic-iasyncenumerable((-0))-system-func((-0-1))-system-func((-0-2))-system-collections-generic-iequalitycomparer((-1))-system-threading-cancellationtoken)
  119. // That one overload covers the next two methods, because it supplies a default comparer.
  120. /// <summary>
  121. /// Creates a lookup from an async-enumerable sequence according to a specified key selector function, and an element selector function.
  122. /// </summary>
  123. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  124. /// <typeparam name="TKey">The type of the lookup key computed for each element in the source sequence.</typeparam>
  125. /// <typeparam name="TElement">The type of the lookup value computed for each element in the source sequence.</typeparam>
  126. /// <param name="source">An async-enumerable sequence to create a lookup for.</param>
  127. /// <param name="keySelector">A function to extract a key from each element.</param>
  128. /// <param name="elementSelector">A transform function to produce a result element value from each element.</param>
  129. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  130. /// <returns>An async-enumerable sequence containing a single element with a lookup mapping unique key values onto the corresponding source sequence's elements.</returns>
  131. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="elementSelector"/> is null.</exception>
  132. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  133. public static ValueTask<ILookup<TKey, TElement>> ToLookupAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, CancellationToken cancellationToken = default) =>
  134. ToLookupAsync(source, keySelector, elementSelector, comparer: null, cancellationToken);
  135. /// <summary>
  136. /// Creates a lookup from an async-enumerable sequence according to a specified key selector function, a comparer, and an element selector function.
  137. /// </summary>
  138. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  139. /// <typeparam name="TKey">The type of the lookup key computed for each element in the source sequence.</typeparam>
  140. /// <typeparam name="TElement">The type of the lookup value computed for each element in the source sequence.</typeparam>
  141. /// <param name="source">An async-enumerable sequence to create a lookup for.</param>
  142. /// <param name="keySelector">A function to extract a key from each element.</param>
  143. /// <param name="elementSelector">A transform function to produce a result element value from each element.</param>
  144. /// <param name="comparer">An equality comparer to compare keys.</param>
  145. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  146. /// <returns>An async-enumerable sequence containing a single element with a lookup mapping unique key values onto the corresponding source sequence's elements.</returns>
  147. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="elementSelector"/> or <paramref name="comparer"/> is null.</exception>
  148. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  149. public static ValueTask<ILookup<TKey, TElement>> ToLookupAsync<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default)
  150. {
  151. if (source == null)
  152. throw Error.ArgumentNull(nameof(source));
  153. if (keySelector == null)
  154. throw Error.ArgumentNull(nameof(keySelector));
  155. if (elementSelector == null)
  156. throw Error.ArgumentNull(nameof(elementSelector));
  157. return Core(source, keySelector, elementSelector, comparer, cancellationToken);
  158. static async ValueTask<ILookup<TKey, TElement>> Core(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  159. {
  160. return await Internal.Lookup<TKey, TElement>.CreateAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false);
  161. }
  162. }
  163. #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES
  164. /// <summary>
  165. /// Creates a lookup from an async-enumerable sequence by invoking key and element selector functions on each source element and awaiting the results.
  166. /// </summary>
  167. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  168. /// <typeparam name="TKey">The type of the lookup key computed for each element in the source sequence.</typeparam>
  169. /// <typeparam name="TElement">The type of the lookup value computed for each element in the source sequence.</typeparam>
  170. /// <param name="source">An async-enumerable sequence to create a lookup for.</param>
  171. /// <param name="keySelector">An asynchronous function to extract a key from each element.</param>
  172. /// <param name="elementSelector">An asynchronous transform function to produce a result element value from each element.</param>
  173. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  174. /// <returns>An async-enumerable sequence containing a single element with a lookup mapping unique key values onto the corresponding source sequence's elements.</returns>
  175. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="elementSelector"/> is null.</exception>
  176. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  177. [GenerateAsyncOverload]
  178. [Obsolete("Use ToLookupAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToLookupAwaitAsync functionality now exists as overloads of ToLookupAsync.")]
  179. private static ValueTask<ILookup<TKey, TElement>> ToLookupAwaitAsyncCore<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, CancellationToken cancellationToken = default) =>
  180. ToLookupAwaitAsyncCore<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer: null, cancellationToken);
  181. /// <summary>
  182. /// Creates a lookup from an async-enumerable sequence by invoking key and element selector functions on each source element and awaiting the results.
  183. /// </summary>
  184. /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
  185. /// <typeparam name="TKey">The type of the lookup key computed for each element in the source sequence.</typeparam>
  186. /// <typeparam name="TElement">The type of the lookup value computed for each element in the source sequence.</typeparam>
  187. /// <param name="source">An async-enumerable sequence to create a lookup for.</param>
  188. /// <param name="keySelector">An asynchronous function to extract a key from each element.</param>
  189. /// <param name="elementSelector">An asynchronous transform function to produce a result element value from each source element.</param>
  190. /// <param name="comparer">An equality comparer to compare keys.</param>
  191. /// <param name="cancellationToken">The optional cancellation token to be used for cancelling the sequence at any time.</param>
  192. /// <returns>A ValueTask containing a lookup mapping unique key values onto the corresponding source sequence's elements.</returns>
  193. /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="elementSelector"/> or <paramref name="comparer"/> is null.</exception>
  194. /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
  195. [GenerateAsyncOverload]
  196. [Obsolete("Use ToLookupAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToLookupAwaitAsync functionality now exists as overloads of ToLookupAsync.")]
  197. private static ValueTask<ILookup<TKey, TElement>> ToLookupAwaitAsyncCore<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default)
  198. {
  199. if (source == null)
  200. throw Error.ArgumentNull(nameof(source));
  201. if (keySelector == null)
  202. throw Error.ArgumentNull(nameof(keySelector));
  203. if (elementSelector == null)
  204. throw Error.ArgumentNull(nameof(elementSelector));
  205. return Core(source, keySelector, elementSelector, comparer, cancellationToken);
  206. static async ValueTask<ILookup<TKey, TElement>> Core(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  207. {
  208. return await Internal.LookupWithTask<TKey, TElement>.CreateAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false);
  209. }
  210. }
  211. #if !NO_DEEP_CANCELLATION
  212. [GenerateAsyncOverload]
  213. [Obsolete("Use ToLookupAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToLookupAwaitWithCancellationAsync functionality now exists as overloads of ToLookupAsync.")]
  214. private static ValueTask<ILookup<TKey, TElement>> ToLookupAwaitWithCancellationAsyncCore<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, CancellationToken cancellationToken = default) =>
  215. ToLookupAwaitWithCancellationAsyncCore(source, keySelector, elementSelector, comparer: null, cancellationToken);
  216. [GenerateAsyncOverload]
  217. [Obsolete("Use ToLookupAsync. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the ToLookupAwaitWithCancellationAsync functionality now exists as overloads of ToLookupAsync.")]
  218. private static ValueTask<ILookup<TKey, TElement>> ToLookupAwaitWithCancellationAsyncCore<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken = default)
  219. {
  220. if (source == null)
  221. throw Error.ArgumentNull(nameof(source));
  222. if (keySelector == null)
  223. throw Error.ArgumentNull(nameof(keySelector));
  224. if (elementSelector == null)
  225. throw Error.ArgumentNull(nameof(elementSelector));
  226. return Core(source, keySelector, elementSelector, comparer, cancellationToken);
  227. static async ValueTask<ILookup<TKey, TElement>> Core(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey>? comparer, CancellationToken cancellationToken)
  228. {
  229. return await Internal.LookupWithTask<TKey, TElement>.CreateAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false);
  230. }
  231. }
  232. #endif
  233. }
  234. }