|
|
@@ -4,6 +4,7 @@
|
|
|
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Diagnostics;
|
|
|
using System.Linq;
|
|
|
using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
@@ -17,30 +18,7 @@ namespace System.Linq
|
|
|
if (source == null)
|
|
|
throw new ArgumentNullException(nameof(source));
|
|
|
|
|
|
- return CreateEnumerable(
|
|
|
- () =>
|
|
|
- {
|
|
|
- var e = source.GetEnumerator();
|
|
|
-
|
|
|
- return CreateEnumerator(
|
|
|
- ct => Task.Run(() =>
|
|
|
- {
|
|
|
- var res = false;
|
|
|
- try
|
|
|
- {
|
|
|
- res = e.MoveNext();
|
|
|
- }
|
|
|
- finally
|
|
|
- {
|
|
|
- if (!res)
|
|
|
- e.Dispose();
|
|
|
- }
|
|
|
- return res;
|
|
|
- }, ct),
|
|
|
- () => e.Current,
|
|
|
- () => e.Dispose()
|
|
|
- );
|
|
|
- });
|
|
|
+ return new AsyncEnumerableAdapter<TSource>(source);
|
|
|
}
|
|
|
|
|
|
public static IAsyncEnumerable<TSource> ToAsyncEnumerable<TSource>(this Task<TSource> task)
|
|
|
@@ -91,5 +69,73 @@ namespace System.Linq
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ internal sealed class AsyncEnumerableAdapter<T> : AsyncIterator<T>, IIListProvider<T>
|
|
|
+ {
|
|
|
+ private readonly IEnumerable<T> source;
|
|
|
+ private IEnumerator<T> enumerator;
|
|
|
+
|
|
|
+ public AsyncEnumerableAdapter(IEnumerable<T> source)
|
|
|
+ {
|
|
|
+ Debug.Assert(source != null);
|
|
|
+ this.source = source;
|
|
|
+ }
|
|
|
+
|
|
|
+ public override AsyncIterator<T> Clone()
|
|
|
+ {
|
|
|
+ return new AsyncEnumerableAdapter<T>(source);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void Dispose()
|
|
|
+ {
|
|
|
+ if (enumerator != null)
|
|
|
+ {
|
|
|
+ enumerator.Dispose();
|
|
|
+ enumerator = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ base.Dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override Task<bool> MoveNextCore(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ switch (state)
|
|
|
+ {
|
|
|
+ case State.Allocated:
|
|
|
+ enumerator = source.GetEnumerator();
|
|
|
+ state = State.Iterating;
|
|
|
+ goto case State.Iterating;
|
|
|
+
|
|
|
+ case State.Iterating:
|
|
|
+ if (enumerator.MoveNext())
|
|
|
+ {
|
|
|
+ current = enumerator.Current;
|
|
|
+ return Task.FromResult(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ Dispose();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return Task.FromResult(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // These optimizations rely on the Sys.Linq impls from IEnumerable to optimize
|
|
|
+ // and short circuit as appropriate
|
|
|
+ public Task<T[]> ToArrayAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ return Task.FromResult(source.ToArray());
|
|
|
+ }
|
|
|
+
|
|
|
+ public Task<List<T>> ToListAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ return Task.FromResult(source.ToList());
|
|
|
+ }
|
|
|
+
|
|
|
+ public Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ return Task.FromResult(source.Count());
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|