Using.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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 AsyncEnumerableEx
  11. {
  12. public static IAsyncEnumerable<TSource> Using<TSource, TResource>(Func<TResource> resourceFactory, Func<TResource, IAsyncEnumerable<TSource>> enumerableFactory) where TResource : IDisposable
  13. {
  14. if (resourceFactory == null)
  15. throw new ArgumentNullException(nameof(resourceFactory));
  16. if (enumerableFactory == null)
  17. throw new ArgumentNullException(nameof(enumerableFactory));
  18. return new UsingAsyncIterator<TSource, TResource>(resourceFactory, enumerableFactory);
  19. }
  20. public static IAsyncEnumerable<TSource> Using<TSource, TResource>(Func<Task<TResource>> resourceFactory, Func<TResource, Task<IAsyncEnumerable<TSource>>> enumerableFactory) where TResource : IDisposable
  21. {
  22. if (resourceFactory == null)
  23. throw new ArgumentNullException(nameof(resourceFactory));
  24. if (enumerableFactory == null)
  25. throw new ArgumentNullException(nameof(enumerableFactory));
  26. return new UsingAsyncIteratorWithTask<TSource, TResource>(resourceFactory, enumerableFactory);
  27. }
  28. private sealed class UsingAsyncIterator<TSource, TResource> : AsyncIterator<TSource> where TResource : IDisposable
  29. {
  30. private readonly Func<TResource, IAsyncEnumerable<TSource>> enumerableFactory;
  31. private readonly Func<TResource> resourceFactory;
  32. private IAsyncEnumerable<TSource> enumerable;
  33. private IAsyncEnumerator<TSource> enumerator;
  34. private TResource resource;
  35. public UsingAsyncIterator(Func<TResource> resourceFactory, Func<TResource, IAsyncEnumerable<TSource>> enumerableFactory)
  36. {
  37. Debug.Assert(resourceFactory != null);
  38. Debug.Assert(enumerableFactory != null);
  39. this.resourceFactory = resourceFactory;
  40. this.enumerableFactory = enumerableFactory;
  41. }
  42. public override AsyncIterator<TSource> Clone()
  43. {
  44. return new UsingAsyncIterator<TSource, TResource>(resourceFactory, enumerableFactory);
  45. }
  46. public override async ValueTask DisposeAsync()
  47. {
  48. if (enumerator != null)
  49. {
  50. await enumerator.DisposeAsync().ConfigureAwait(false);
  51. enumerator = null;
  52. }
  53. if (resource != null)
  54. {
  55. resource.Dispose();
  56. resource = default;
  57. }
  58. await base.DisposeAsync().ConfigureAwait(false);
  59. }
  60. protected override async ValueTask<bool> MoveNextCore(CancellationToken cancellationToken)
  61. {
  62. switch (state)
  63. {
  64. case AsyncIteratorState.Allocated:
  65. enumerator = enumerable.GetAsyncEnumerator(cancellationToken);
  66. state = AsyncIteratorState.Iterating;
  67. goto case AsyncIteratorState.Iterating;
  68. case AsyncIteratorState.Iterating:
  69. while (await enumerator.MoveNextAsync().ConfigureAwait(false))
  70. {
  71. current = enumerator.Current;
  72. return true;
  73. }
  74. await DisposeAsync().ConfigureAwait(false);
  75. break;
  76. }
  77. return false;
  78. }
  79. protected override void OnGetEnumerator(CancellationToken cancellationToken)
  80. {
  81. // REVIEW: Wire cancellation to the functions.
  82. resource = resourceFactory();
  83. enumerable = enumerableFactory(resource);
  84. base.OnGetEnumerator(cancellationToken);
  85. }
  86. }
  87. private sealed class UsingAsyncIteratorWithTask<TSource, TResource> : AsyncIterator<TSource> where TResource : IDisposable
  88. {
  89. private readonly Func<TResource, Task<IAsyncEnumerable<TSource>>> enumerableFactory;
  90. private readonly Func<Task<TResource>> resourceFactory;
  91. private IAsyncEnumerable<TSource> enumerable;
  92. private IAsyncEnumerator<TSource> enumerator;
  93. private TResource resource;
  94. public UsingAsyncIteratorWithTask(Func<Task<TResource>> resourceFactory, Func<TResource, Task<IAsyncEnumerable<TSource>>> enumerableFactory)
  95. {
  96. Debug.Assert(resourceFactory != null);
  97. Debug.Assert(enumerableFactory != null);
  98. this.resourceFactory = resourceFactory;
  99. this.enumerableFactory = enumerableFactory;
  100. }
  101. public override AsyncIterator<TSource> Clone()
  102. {
  103. return new UsingAsyncIteratorWithTask<TSource, TResource>(resourceFactory, enumerableFactory);
  104. }
  105. public override async ValueTask DisposeAsync()
  106. {
  107. if (enumerator != null)
  108. {
  109. await enumerator.DisposeAsync().ConfigureAwait(false);
  110. enumerator = null;
  111. }
  112. if (resource != null)
  113. {
  114. resource.Dispose();
  115. resource = default;
  116. }
  117. await base.DisposeAsync().ConfigureAwait(false);
  118. }
  119. protected override async ValueTask<bool> MoveNextCore(CancellationToken cancellationToken)
  120. {
  121. switch (state)
  122. {
  123. case AsyncIteratorState.Allocated:
  124. resource = await resourceFactory().ConfigureAwait(false);
  125. enumerable = await enumerableFactory(resource).ConfigureAwait(false);
  126. enumerator = enumerable.GetAsyncEnumerator(cancellationToken);
  127. state = AsyncIteratorState.Iterating;
  128. goto case AsyncIteratorState.Iterating;
  129. case AsyncIteratorState.Iterating:
  130. while (await enumerator.MoveNextAsync().ConfigureAwait(false))
  131. {
  132. current = enumerator.Current;
  133. return true;
  134. }
  135. await DisposeAsync().ConfigureAwait(false);
  136. break;
  137. }
  138. return false;
  139. }
  140. }
  141. }
  142. }