Distinct.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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.Diagnostics;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. namespace System.Linq
  9. {
  10. public static partial class AsyncEnumerable
  11. {
  12. public static IAsyncEnumerable<TSource> Distinct<TSource>(this IAsyncEnumerable<TSource> source)
  13. {
  14. if (source == null)
  15. throw new ArgumentNullException(nameof(source));
  16. return source.Distinct(EqualityComparer<TSource>.Default);
  17. }
  18. public static IAsyncEnumerable<TSource> Distinct<TSource>(this IAsyncEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
  19. {
  20. if (source == null)
  21. throw new ArgumentNullException(nameof(source));
  22. if (comparer == null)
  23. throw new ArgumentNullException(nameof(comparer));
  24. return new DistinctAsyncIterator<TSource>(source, comparer);
  25. }
  26. public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
  27. {
  28. if (source == null)
  29. throw new ArgumentNullException(nameof(source));
  30. if (keySelector == null)
  31. throw new ArgumentNullException(nameof(keySelector));
  32. return source.Distinct(keySelector, EqualityComparer<TKey>.Default);
  33. }
  34. public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
  35. {
  36. if (source == null)
  37. throw new ArgumentNullException(nameof(source));
  38. if (keySelector == null)
  39. throw new ArgumentNullException(nameof(keySelector));
  40. if (comparer == null)
  41. throw new ArgumentNullException(nameof(comparer));
  42. return new DistinctAsyncIterator<TSource, TKey>(source, keySelector, comparer);
  43. }
  44. public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector)
  45. {
  46. if (source == null)
  47. throw new ArgumentNullException(nameof(source));
  48. if (keySelector == null)
  49. throw new ArgumentNullException(nameof(keySelector));
  50. return source.Distinct(keySelector, EqualityComparer<TKey>.Default);
  51. }
  52. public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector, IEqualityComparer<TKey> comparer)
  53. {
  54. if (source == null)
  55. throw new ArgumentNullException(nameof(source));
  56. if (keySelector == null)
  57. throw new ArgumentNullException(nameof(keySelector));
  58. if (comparer == null)
  59. throw new ArgumentNullException(nameof(comparer));
  60. return new DistinctAsyncIteratorWithTask<TSource, TKey>(source, keySelector, comparer);
  61. }
  62. private sealed class DistinctAsyncIterator<TSource> : AsyncIterator<TSource>, IIListProvider<TSource>
  63. {
  64. private readonly IEqualityComparer<TSource> comparer;
  65. private readonly IAsyncEnumerable<TSource> source;
  66. private IAsyncEnumerator<TSource> enumerator;
  67. private Set<TSource> set;
  68. public DistinctAsyncIterator(IAsyncEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
  69. {
  70. Debug.Assert(source != null);
  71. this.source = source;
  72. this.comparer = comparer;
  73. }
  74. public async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
  75. {
  76. var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
  77. return s.ToArray();
  78. }
  79. public async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
  80. {
  81. var s = await FillSetAsync(cancellationToken)
  82. .ConfigureAwait(false);
  83. return s.ToList();
  84. }
  85. public async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  86. {
  87. return onlyIfCheap ? -1 : (await FillSetAsync(cancellationToken).ConfigureAwait(false)).Count;
  88. }
  89. public override AsyncIterator<TSource> Clone()
  90. {
  91. return new DistinctAsyncIterator<TSource>(source, comparer);
  92. }
  93. public override async Task DisposeAsync()
  94. {
  95. if (enumerator != null)
  96. {
  97. await enumerator.DisposeAsync().ConfigureAwait(false);
  98. enumerator = null;
  99. set = null;
  100. }
  101. await base.DisposeAsync().ConfigureAwait(false);
  102. }
  103. protected override async Task<bool> MoveNextCore()
  104. {
  105. switch (state)
  106. {
  107. case AsyncIteratorState.Allocated:
  108. enumerator = source.GetAsyncEnumerator();
  109. if (!await enumerator.MoveNextAsync().ConfigureAwait(false))
  110. {
  111. await DisposeAsync().ConfigureAwait(false);
  112. return false;
  113. }
  114. var element = enumerator.Current;
  115. set = new Set<TSource>(comparer);
  116. set.Add(element);
  117. current = element;
  118. state = AsyncIteratorState.Iterating;
  119. return true;
  120. case AsyncIteratorState.Iterating:
  121. while (await enumerator.MoveNextAsync().ConfigureAwait(false))
  122. {
  123. element = enumerator.Current;
  124. if (set.Add(element))
  125. {
  126. current = element;
  127. return true;
  128. }
  129. }
  130. break;
  131. }
  132. await DisposeAsync().ConfigureAwait(false);
  133. return false;
  134. }
  135. private async Task<Set<TSource>> FillSetAsync(CancellationToken cancellationToken)
  136. {
  137. var s = new Set<TSource>(comparer);
  138. var enu = source.GetAsyncEnumerator();
  139. try
  140. {
  141. while (await enu.MoveNextAsync(cancellationToken).ConfigureAwait(false))
  142. {
  143. s.Add(enu.Current);
  144. }
  145. }
  146. finally
  147. {
  148. await enu.DisposeAsync().ConfigureAwait(false);
  149. }
  150. return s;
  151. }
  152. }
  153. private sealed class DistinctAsyncIterator<TSource, TKey> : AsyncIterator<TSource>, IIListProvider<TSource>
  154. {
  155. private readonly IEqualityComparer<TKey> comparer;
  156. private readonly Func<TSource, TKey> keySelector;
  157. private readonly IAsyncEnumerable<TSource> source;
  158. private IAsyncEnumerator<TSource> enumerator;
  159. private Set<TKey> set;
  160. public DistinctAsyncIterator(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
  161. {
  162. Debug.Assert(source != null);
  163. Debug.Assert(keySelector != null);
  164. Debug.Assert(comparer != null);
  165. this.source = source;
  166. this.keySelector = keySelector;
  167. this.comparer = comparer;
  168. }
  169. public async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
  170. {
  171. var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
  172. return s.ToArray();
  173. }
  174. public async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
  175. {
  176. var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
  177. return s;
  178. }
  179. public async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  180. {
  181. if (onlyIfCheap)
  182. {
  183. return -1;
  184. }
  185. var count = 0;
  186. var s = new Set<TKey>(comparer);
  187. var enu = source.GetAsyncEnumerator();
  188. try
  189. {
  190. while (await enu.MoveNextAsync().ConfigureAwait(false))
  191. {
  192. var item = enu.Current;
  193. if (s.Add(keySelector(item)))
  194. {
  195. count++;
  196. }
  197. }
  198. }
  199. finally
  200. {
  201. await enu.DisposeAsync().ConfigureAwait(false);
  202. }
  203. return count;
  204. }
  205. public override AsyncIterator<TSource> Clone()
  206. {
  207. return new DistinctAsyncIterator<TSource, TKey>(source, keySelector, comparer);
  208. }
  209. public override async Task DisposeAsync()
  210. {
  211. if (enumerator != null)
  212. {
  213. await enumerator.DisposeAsync().ConfigureAwait(false);
  214. enumerator = null;
  215. set = null;
  216. }
  217. await base.DisposeAsync().ConfigureAwait(false);
  218. }
  219. protected override async Task<bool> MoveNextCore()
  220. {
  221. switch (state)
  222. {
  223. case AsyncIteratorState.Allocated:
  224. enumerator = source.GetAsyncEnumerator();
  225. if (!await enumerator.MoveNextAsync().ConfigureAwait(false))
  226. {
  227. await DisposeAsync().ConfigureAwait(false);
  228. return false;
  229. }
  230. var element = enumerator.Current;
  231. set = new Set<TKey>(comparer);
  232. set.Add(keySelector(element));
  233. current = element;
  234. state = AsyncIteratorState.Iterating;
  235. return true;
  236. case AsyncIteratorState.Iterating:
  237. while (await enumerator.MoveNextAsync().ConfigureAwait(false))
  238. {
  239. element = enumerator.Current;
  240. if (set.Add(keySelector(element)))
  241. {
  242. current = element;
  243. return true;
  244. }
  245. }
  246. break;
  247. }
  248. await DisposeAsync().ConfigureAwait(false);
  249. return false;
  250. }
  251. private async Task<List<TSource>> FillSetAsync(CancellationToken cancellationToken)
  252. {
  253. var s = new Set<TKey>(comparer);
  254. var r = new List<TSource>();
  255. var enu = source.GetAsyncEnumerator();
  256. try
  257. {
  258. while (await enu.MoveNextAsync(cancellationToken).ConfigureAwait(false))
  259. {
  260. var item = enu.Current;
  261. if (s.Add(keySelector(item)))
  262. {
  263. r.Add(item);
  264. }
  265. }
  266. }
  267. finally
  268. {
  269. await enu.DisposeAsync().ConfigureAwait(false);
  270. }
  271. return r;
  272. }
  273. }
  274. private sealed class DistinctAsyncIteratorWithTask<TSource, TKey> : AsyncIterator<TSource>, IIListProvider<TSource>
  275. {
  276. private readonly IEqualityComparer<TKey> comparer;
  277. private readonly Func<TSource, Task<TKey>> keySelector;
  278. private readonly IAsyncEnumerable<TSource> source;
  279. private IAsyncEnumerator<TSource> enumerator;
  280. private Set<TKey> set;
  281. public DistinctAsyncIteratorWithTask(IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector, IEqualityComparer<TKey> comparer)
  282. {
  283. Debug.Assert(source != null);
  284. Debug.Assert(keySelector != null);
  285. Debug.Assert(comparer != null);
  286. this.source = source;
  287. this.keySelector = keySelector;
  288. this.comparer = comparer;
  289. }
  290. public async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
  291. {
  292. var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
  293. return s.ToArray();
  294. }
  295. public async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
  296. {
  297. var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
  298. return s;
  299. }
  300. public async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
  301. {
  302. if (onlyIfCheap)
  303. {
  304. return -1;
  305. }
  306. var count = 0;
  307. var s = new Set<TKey>(comparer);
  308. var enu = source.GetAsyncEnumerator();
  309. try
  310. {
  311. while (await enu.MoveNextAsync().ConfigureAwait(false))
  312. {
  313. var item = enu.Current;
  314. if (s.Add(await keySelector(item).ConfigureAwait(false)))
  315. {
  316. count++;
  317. }
  318. }
  319. }
  320. finally
  321. {
  322. await enu.DisposeAsync().ConfigureAwait(false);
  323. }
  324. return count;
  325. }
  326. public override AsyncIterator<TSource> Clone()
  327. {
  328. return new DistinctAsyncIteratorWithTask<TSource, TKey>(source, keySelector, comparer);
  329. }
  330. public override async Task DisposeAsync()
  331. {
  332. if (enumerator != null)
  333. {
  334. await enumerator.DisposeAsync().ConfigureAwait(false);
  335. enumerator = null;
  336. set = null;
  337. }
  338. await base.DisposeAsync().ConfigureAwait(false);
  339. }
  340. protected override async Task<bool> MoveNextCore()
  341. {
  342. switch (state)
  343. {
  344. case AsyncIteratorState.Allocated:
  345. enumerator = source.GetAsyncEnumerator();
  346. if (!await enumerator.MoveNextAsync().ConfigureAwait(false))
  347. {
  348. await DisposeAsync().ConfigureAwait(false);
  349. return false;
  350. }
  351. var element = enumerator.Current;
  352. set = new Set<TKey>(comparer);
  353. set.Add(await keySelector(element).ConfigureAwait(false));
  354. current = element;
  355. state = AsyncIteratorState.Iterating;
  356. return true;
  357. case AsyncIteratorState.Iterating:
  358. while (await enumerator.MoveNextAsync().ConfigureAwait(false))
  359. {
  360. element = enumerator.Current;
  361. if (set.Add(await keySelector(element).ConfigureAwait(false)))
  362. {
  363. current = element;
  364. return true;
  365. }
  366. }
  367. break;
  368. }
  369. await DisposeAsync().ConfigureAwait(false);
  370. return false;
  371. }
  372. private async Task<List<TSource>> FillSetAsync(CancellationToken cancellationToken)
  373. {
  374. var s = new Set<TKey>(comparer);
  375. var r = new List<TSource>();
  376. var enu = source.GetAsyncEnumerator();
  377. try
  378. {
  379. while (await enu.MoveNextAsync(cancellationToken).ConfigureAwait(false))
  380. {
  381. var item = enu.Current;
  382. if (s.Add(await keySelector(item).ConfigureAwait(false)))
  383. {
  384. r.Add(item);
  385. }
  386. }
  387. }
  388. finally
  389. {
  390. await enu.DisposeAsync().ConfigureAwait(false);
  391. }
  392. return r;
  393. }
  394. }
  395. }
  396. }