GroupJoin.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace System.Linq
  10. {
  11. public static partial class AsyncEnumerable
  12. {
  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 new ArgumentNullException(nameof(outer));
  17. if (inner == null)
  18. throw new ArgumentNullException(nameof(inner));
  19. if (outerKeySelector == null)
  20. throw new ArgumentNullException(nameof(outerKeySelector));
  21. if (innerKeySelector == null)
  22. throw new ArgumentNullException(nameof(innerKeySelector));
  23. if (resultSelector == null)
  24. throw new ArgumentNullException(nameof(resultSelector));
  25. if (comparer == null)
  26. throw new ArgumentNullException(nameof(comparer));
  27. return new GroupJoinAsyncEnumerable<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
  28. }
  29. 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)
  30. {
  31. if (outer == null)
  32. throw new ArgumentNullException(nameof(outer));
  33. if (inner == null)
  34. throw new ArgumentNullException(nameof(inner));
  35. if (outerKeySelector == null)
  36. throw new ArgumentNullException(nameof(outerKeySelector));
  37. if (innerKeySelector == null)
  38. throw new ArgumentNullException(nameof(innerKeySelector));
  39. if (resultSelector == null)
  40. throw new ArgumentNullException(nameof(resultSelector));
  41. return outer.GroupJoin(inner, outerKeySelector, innerKeySelector, resultSelector, EqualityComparer<TKey>.Default);
  42. }
  43. private sealed class GroupJoinAsyncEnumerable<TOuter, TInner, TKey, TResult> : IAsyncEnumerable<TResult>
  44. {
  45. private readonly IEqualityComparer<TKey> _comparer;
  46. private readonly IAsyncEnumerable<TInner> _inner;
  47. private readonly Func<TInner, TKey> _innerKeySelector;
  48. private readonly IAsyncEnumerable<TOuter> _outer;
  49. private readonly Func<TOuter, TKey> _outerKeySelector;
  50. private readonly Func<TOuter, IAsyncEnumerable<TInner>, TResult> _resultSelector;
  51. public GroupJoinAsyncEnumerable(
  52. IAsyncEnumerable<TOuter> outer,
  53. IAsyncEnumerable<TInner> inner,
  54. Func<TOuter, TKey> outerKeySelector,
  55. Func<TInner, TKey> innerKeySelector,
  56. Func<TOuter, IAsyncEnumerable<TInner>, TResult> resultSelector,
  57. IEqualityComparer<TKey> comparer)
  58. {
  59. _outer = outer;
  60. _inner = inner;
  61. _outerKeySelector = outerKeySelector;
  62. _innerKeySelector = innerKeySelector;
  63. _resultSelector = resultSelector;
  64. _comparer = comparer;
  65. }
  66. public IAsyncEnumerator<TResult> GetEnumerator()
  67. => new GroupJoinAsyncEnumerator(
  68. _outer.GetEnumerator(),
  69. _inner,
  70. _outerKeySelector,
  71. _innerKeySelector,
  72. _resultSelector,
  73. _comparer);
  74. private sealed class GroupJoinAsyncEnumerator : IAsyncEnumerator<TResult>
  75. {
  76. private readonly IEqualityComparer<TKey> _comparer;
  77. private readonly IAsyncEnumerable<TInner> _inner;
  78. private readonly Func<TInner, TKey> _innerKeySelector;
  79. private readonly IAsyncEnumerator<TOuter> _outer;
  80. private readonly Func<TOuter, TKey> _outerKeySelector;
  81. private readonly Func<TOuter, IAsyncEnumerable<TInner>, TResult> _resultSelector;
  82. private Internal.Lookup<TKey, TInner> _lookup;
  83. public GroupJoinAsyncEnumerator(
  84. IAsyncEnumerator<TOuter> outer,
  85. IAsyncEnumerable<TInner> inner,
  86. Func<TOuter, TKey> outerKeySelector,
  87. Func<TInner, TKey> innerKeySelector,
  88. Func<TOuter, IAsyncEnumerable<TInner>, TResult> resultSelector,
  89. IEqualityComparer<TKey> comparer)
  90. {
  91. _outer = outer;
  92. _inner = inner;
  93. _outerKeySelector = outerKeySelector;
  94. _innerKeySelector = innerKeySelector;
  95. _resultSelector = resultSelector;
  96. _comparer = comparer;
  97. }
  98. public async Task<bool> MoveNext(CancellationToken cancellationToken)
  99. {
  100. // nothing to do
  101. if (!await _outer.MoveNext(cancellationToken)
  102. .ConfigureAwait(false))
  103. {
  104. return false;
  105. }
  106. if (_lookup == null)
  107. {
  108. _lookup = await Internal.Lookup<TKey, TInner>.CreateForJoinAsync(_inner, _innerKeySelector, _comparer, cancellationToken)
  109. .ConfigureAwait(false);
  110. }
  111. var item = _outer.Current;
  112. Current = _resultSelector(item, _lookup[_outerKeySelector(item)].ToAsyncEnumerable());
  113. return true;
  114. }
  115. public TResult Current { get; private set; }
  116. public void Dispose()
  117. {
  118. _outer.Dispose();
  119. }
  120. }
  121. }
  122. }
  123. }