GroupJoin.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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.Threading;
  6. using System.Threading.Tasks;
  7. namespace System.Linq
  8. {
  9. public static partial class AsyncEnumerable
  10. {
  11. public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, TResult> resultSelector) =>
  12. GroupJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer: null);
  13. public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, TResult> resultSelector, IEqualityComparer<TKey> comparer)
  14. {
  15. if (outer == null)
  16. throw Error.ArgumentNull(nameof(outer));
  17. if (inner == null)
  18. throw Error.ArgumentNull(nameof(inner));
  19. if (outerKeySelector == null)
  20. throw Error.ArgumentNull(nameof(outerKeySelector));
  21. if (innerKeySelector == null)
  22. throw Error.ArgumentNull(nameof(innerKeySelector));
  23. if (resultSelector == null)
  24. throw Error.ArgumentNull(nameof(resultSelector));
  25. return new GroupJoinAsyncEnumerable<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
  26. }
  27. public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, ValueTask<TKey>> outerKeySelector, Func<TInner, ValueTask<TKey>> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, ValueTask<TResult>> resultSelector) =>
  28. GroupJoin<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer: null);
  29. public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, ValueTask<TKey>> outerKeySelector, Func<TInner, ValueTask<TKey>> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
  30. {
  31. if (outer == null)
  32. throw Error.ArgumentNull(nameof(outer));
  33. if (inner == null)
  34. throw Error.ArgumentNull(nameof(inner));
  35. if (outerKeySelector == null)
  36. throw Error.ArgumentNull(nameof(outerKeySelector));
  37. if (innerKeySelector == null)
  38. throw Error.ArgumentNull(nameof(innerKeySelector));
  39. if (resultSelector == null)
  40. throw Error.ArgumentNull(nameof(resultSelector));
  41. return new GroupJoinAsyncEnumerableWithTask<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
  42. }
  43. #if !NO_DEEP_CANCELLATION
  44. public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector, Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> resultSelector) =>
  45. GroupJoin<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer: null);
  46. public static IAsyncEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IAsyncEnumerable<TOuter> outer, IAsyncEnumerable<TInner> inner, Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector, Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector, Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
  47. {
  48. if (outer == null)
  49. throw Error.ArgumentNull(nameof(outer));
  50. if (inner == null)
  51. throw Error.ArgumentNull(nameof(inner));
  52. if (outerKeySelector == null)
  53. throw Error.ArgumentNull(nameof(outerKeySelector));
  54. if (innerKeySelector == null)
  55. throw Error.ArgumentNull(nameof(innerKeySelector));
  56. if (resultSelector == null)
  57. throw Error.ArgumentNull(nameof(resultSelector));
  58. return new GroupJoinAsyncEnumerableWithTaskAndCancellation<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
  59. }
  60. #endif
  61. private sealed class GroupJoinAsyncEnumerable<TOuter, TInner, TKey, TResult> : IAsyncEnumerable<TResult>
  62. {
  63. private readonly IEqualityComparer<TKey> _comparer;
  64. private readonly IAsyncEnumerable<TInner> _inner;
  65. private readonly Func<TInner, TKey> _innerKeySelector;
  66. private readonly IAsyncEnumerable<TOuter> _outer;
  67. private readonly Func<TOuter, TKey> _outerKeySelector;
  68. private readonly Func<TOuter, IAsyncEnumerable<TInner>, TResult> _resultSelector;
  69. public GroupJoinAsyncEnumerable(
  70. IAsyncEnumerable<TOuter> outer,
  71. IAsyncEnumerable<TInner> inner,
  72. Func<TOuter, TKey> outerKeySelector,
  73. Func<TInner, TKey> innerKeySelector,
  74. Func<TOuter, IAsyncEnumerable<TInner>, TResult> resultSelector,
  75. IEqualityComparer<TKey> comparer)
  76. {
  77. _outer = outer;
  78. _inner = inner;
  79. _outerKeySelector = outerKeySelector;
  80. _innerKeySelector = innerKeySelector;
  81. _resultSelector = resultSelector;
  82. _comparer = comparer;
  83. }
  84. public IAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken)
  85. {
  86. cancellationToken.ThrowIfCancellationRequested(); // NB: [LDM-2018-11-28] Equivalent to async iterator behavior.
  87. return new GroupJoinAsyncEnumerator(
  88. _outer.GetAsyncEnumerator(cancellationToken),
  89. _inner,
  90. _outerKeySelector,
  91. _innerKeySelector,
  92. _resultSelector,
  93. _comparer,
  94. cancellationToken);
  95. }
  96. private sealed class GroupJoinAsyncEnumerator : IAsyncEnumerator<TResult>
  97. {
  98. private readonly IEqualityComparer<TKey> _comparer;
  99. private readonly IAsyncEnumerable<TInner> _inner;
  100. private readonly Func<TInner, TKey> _innerKeySelector;
  101. private readonly IAsyncEnumerator<TOuter> _outer;
  102. private readonly Func<TOuter, TKey> _outerKeySelector;
  103. private readonly Func<TOuter, IAsyncEnumerable<TInner>, TResult> _resultSelector;
  104. private readonly CancellationToken _cancellationToken;
  105. private Internal.Lookup<TKey, TInner> _lookup;
  106. public GroupJoinAsyncEnumerator(
  107. IAsyncEnumerator<TOuter> outer,
  108. IAsyncEnumerable<TInner> inner,
  109. Func<TOuter, TKey> outerKeySelector,
  110. Func<TInner, TKey> innerKeySelector,
  111. Func<TOuter, IAsyncEnumerable<TInner>, TResult> resultSelector,
  112. IEqualityComparer<TKey> comparer,
  113. CancellationToken cancellationToken)
  114. {
  115. _outer = outer;
  116. _inner = inner;
  117. _outerKeySelector = outerKeySelector;
  118. _innerKeySelector = innerKeySelector;
  119. _resultSelector = resultSelector;
  120. _comparer = comparer;
  121. _cancellationToken = cancellationToken;
  122. }
  123. public async ValueTask<bool> MoveNextAsync()
  124. {
  125. // nothing to do
  126. if (!await _outer.MoveNextAsync().ConfigureAwait(false))
  127. {
  128. return false;
  129. }
  130. if (_lookup == null)
  131. {
  132. _lookup = await Internal.Lookup<TKey, TInner>.CreateForJoinAsync(_inner, _innerKeySelector, _comparer, _cancellationToken).ConfigureAwait(false);
  133. }
  134. var item = _outer.Current;
  135. var outerKey = _outerKeySelector(item);
  136. var inner = _lookup[outerKey].ToAsyncEnumerable();
  137. Current = _resultSelector(item, inner);
  138. return true;
  139. }
  140. public TResult Current { get; private set; }
  141. public ValueTask DisposeAsync() => _outer.DisposeAsync();
  142. }
  143. }
  144. private sealed class GroupJoinAsyncEnumerableWithTask<TOuter, TInner, TKey, TResult> : IAsyncEnumerable<TResult>
  145. {
  146. private readonly IEqualityComparer<TKey> _comparer;
  147. private readonly IAsyncEnumerable<TInner> _inner;
  148. private readonly Func<TInner, ValueTask<TKey>> _innerKeySelector;
  149. private readonly IAsyncEnumerable<TOuter> _outer;
  150. private readonly Func<TOuter, ValueTask<TKey>> _outerKeySelector;
  151. private readonly Func<TOuter, IAsyncEnumerable<TInner>, ValueTask<TResult>> _resultSelector;
  152. public GroupJoinAsyncEnumerableWithTask(
  153. IAsyncEnumerable<TOuter> outer,
  154. IAsyncEnumerable<TInner> inner,
  155. Func<TOuter, ValueTask<TKey>> outerKeySelector,
  156. Func<TInner, ValueTask<TKey>> innerKeySelector,
  157. Func<TOuter, IAsyncEnumerable<TInner>, ValueTask<TResult>> resultSelector,
  158. IEqualityComparer<TKey> comparer)
  159. {
  160. _outer = outer;
  161. _inner = inner;
  162. _outerKeySelector = outerKeySelector;
  163. _innerKeySelector = innerKeySelector;
  164. _resultSelector = resultSelector;
  165. _comparer = comparer;
  166. }
  167. public IAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken)
  168. {
  169. cancellationToken.ThrowIfCancellationRequested(); // NB: [LDM-2018-11-28] Equivalent to async iterator behavior.
  170. return new GroupJoinAsyncEnumeratorWithTask(
  171. _outer.GetAsyncEnumerator(cancellationToken),
  172. _inner,
  173. _outerKeySelector,
  174. _innerKeySelector,
  175. _resultSelector,
  176. _comparer,
  177. cancellationToken);
  178. }
  179. private sealed class GroupJoinAsyncEnumeratorWithTask : IAsyncEnumerator<TResult>
  180. {
  181. private readonly IEqualityComparer<TKey> _comparer;
  182. private readonly IAsyncEnumerable<TInner> _inner;
  183. private readonly Func<TInner, ValueTask<TKey>> _innerKeySelector;
  184. private readonly IAsyncEnumerator<TOuter> _outer;
  185. private readonly Func<TOuter, ValueTask<TKey>> _outerKeySelector;
  186. private readonly Func<TOuter, IAsyncEnumerable<TInner>, ValueTask<TResult>> _resultSelector;
  187. private readonly CancellationToken _cancellationToken;
  188. private Internal.LookupWithTask<TKey, TInner> _lookup;
  189. public GroupJoinAsyncEnumeratorWithTask(
  190. IAsyncEnumerator<TOuter> outer,
  191. IAsyncEnumerable<TInner> inner,
  192. Func<TOuter, ValueTask<TKey>> outerKeySelector,
  193. Func<TInner, ValueTask<TKey>> innerKeySelector,
  194. Func<TOuter, IAsyncEnumerable<TInner>, ValueTask<TResult>> resultSelector,
  195. IEqualityComparer<TKey> comparer,
  196. CancellationToken cancellationToken)
  197. {
  198. _outer = outer;
  199. _inner = inner;
  200. _outerKeySelector = outerKeySelector;
  201. _innerKeySelector = innerKeySelector;
  202. _resultSelector = resultSelector;
  203. _comparer = comparer;
  204. _cancellationToken = cancellationToken;
  205. }
  206. public async ValueTask<bool> MoveNextAsync()
  207. {
  208. // nothing to do
  209. if (!await _outer.MoveNextAsync().ConfigureAwait(false))
  210. {
  211. return false;
  212. }
  213. if (_lookup == null)
  214. {
  215. _lookup = await Internal.LookupWithTask<TKey, TInner>.CreateForJoinAsync(_inner, _innerKeySelector, _comparer, _cancellationToken).ConfigureAwait(false);
  216. }
  217. var item = _outer.Current;
  218. var outerKey = await _outerKeySelector(item).ConfigureAwait(false);
  219. var inner = _lookup[outerKey].ToAsyncEnumerable();
  220. Current = await _resultSelector(item, inner).ConfigureAwait(false);
  221. return true;
  222. }
  223. public TResult Current { get; private set; }
  224. public ValueTask DisposeAsync() => _outer.DisposeAsync();
  225. }
  226. }
  227. #if !NO_DEEP_CANCELLATION
  228. private sealed class GroupJoinAsyncEnumerableWithTaskAndCancellation<TOuter, TInner, TKey, TResult> : IAsyncEnumerable<TResult>
  229. {
  230. private readonly IEqualityComparer<TKey> _comparer;
  231. private readonly IAsyncEnumerable<TInner> _inner;
  232. private readonly Func<TInner, CancellationToken, ValueTask<TKey>> _innerKeySelector;
  233. private readonly IAsyncEnumerable<TOuter> _outer;
  234. private readonly Func<TOuter, CancellationToken, ValueTask<TKey>> _outerKeySelector;
  235. private readonly Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> _resultSelector;
  236. public GroupJoinAsyncEnumerableWithTaskAndCancellation(
  237. IAsyncEnumerable<TOuter> outer,
  238. IAsyncEnumerable<TInner> inner,
  239. Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector,
  240. Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector,
  241. Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> resultSelector,
  242. IEqualityComparer<TKey> comparer)
  243. {
  244. _outer = outer;
  245. _inner = inner;
  246. _outerKeySelector = outerKeySelector;
  247. _innerKeySelector = innerKeySelector;
  248. _resultSelector = resultSelector;
  249. _comparer = comparer;
  250. }
  251. public IAsyncEnumerator<TResult> GetAsyncEnumerator(CancellationToken cancellationToken)
  252. {
  253. cancellationToken.ThrowIfCancellationRequested(); // NB: [LDM-2018-11-28] Equivalent to async iterator behavior.
  254. return new GroupJoinAsyncEnumeratorWithTask(
  255. _outer.GetAsyncEnumerator(cancellationToken),
  256. _inner,
  257. _outerKeySelector,
  258. _innerKeySelector,
  259. _resultSelector,
  260. _comparer,
  261. cancellationToken);
  262. }
  263. private sealed class GroupJoinAsyncEnumeratorWithTask : IAsyncEnumerator<TResult>
  264. {
  265. private readonly IEqualityComparer<TKey> _comparer;
  266. private readonly IAsyncEnumerable<TInner> _inner;
  267. private readonly Func<TInner, CancellationToken, ValueTask<TKey>> _innerKeySelector;
  268. private readonly IAsyncEnumerator<TOuter> _outer;
  269. private readonly Func<TOuter, CancellationToken, ValueTask<TKey>> _outerKeySelector;
  270. private readonly Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> _resultSelector;
  271. private readonly CancellationToken _cancellationToken;
  272. private Internal.LookupWithTask<TKey, TInner> _lookup;
  273. public GroupJoinAsyncEnumeratorWithTask(
  274. IAsyncEnumerator<TOuter> outer,
  275. IAsyncEnumerable<TInner> inner,
  276. Func<TOuter, CancellationToken, ValueTask<TKey>> outerKeySelector,
  277. Func<TInner, CancellationToken, ValueTask<TKey>> innerKeySelector,
  278. Func<TOuter, IAsyncEnumerable<TInner>, CancellationToken, ValueTask<TResult>> resultSelector,
  279. IEqualityComparer<TKey> comparer,
  280. CancellationToken cancellationToken)
  281. {
  282. _outer = outer;
  283. _inner = inner;
  284. _outerKeySelector = outerKeySelector;
  285. _innerKeySelector = innerKeySelector;
  286. _resultSelector = resultSelector;
  287. _comparer = comparer;
  288. _cancellationToken = cancellationToken;
  289. }
  290. public async ValueTask<bool> MoveNextAsync()
  291. {
  292. // nothing to do
  293. if (!await _outer.MoveNextAsync().ConfigureAwait(false))
  294. {
  295. return false;
  296. }
  297. if (_lookup == null)
  298. {
  299. _lookup = await Internal.LookupWithTask<TKey, TInner>.CreateForJoinAsync(_inner, _innerKeySelector, _comparer, _cancellationToken).ConfigureAwait(false);
  300. }
  301. var item = _outer.Current;
  302. var outerKey = await _outerKeySelector(item, _cancellationToken).ConfigureAwait(false);
  303. var inner = _lookup[outerKey].ToAsyncEnumerable();
  304. Current = await _resultSelector(item, inner, _cancellationToken).ConfigureAwait(false);
  305. return true;
  306. }
  307. public TResult Current { get; private set; }
  308. public ValueTask DisposeAsync() => _outer.DisposeAsync();
  309. }
  310. }
  311. #endif
  312. }
  313. }