|
@@ -41,6 +41,18 @@ namespace System.Linq
|
|
|
return new GroupedAsyncEnumerableWithTask<TSource, TKey>(source, keySelector, comparer: null);
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ public static IAsyncEnumerable<IAsyncGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector)
|
|
|
+ {
|
|
|
+ if (source == null)
|
|
|
+ throw Error.ArgumentNull(nameof(source));
|
|
|
+ if (keySelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(keySelector));
|
|
|
+
|
|
|
+ return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(source, keySelector, comparer: null);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
public static IAsyncEnumerable<IAsyncGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
|
|
|
{
|
|
|
if (source == null)
|
|
@@ -51,6 +63,18 @@ namespace System.Linq
|
|
|
return new GroupedAsyncEnumerableWithTask<TSource, TKey>(source, keySelector, comparer);
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ public static IAsyncEnumerable<IAsyncGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
|
|
|
+ {
|
|
|
+ if (source == null)
|
|
|
+ throw Error.ArgumentNull(nameof(source));
|
|
|
+ if (keySelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(keySelector));
|
|
|
+
|
|
|
+ return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(source, keySelector, comparer);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
public static IAsyncEnumerable<IAsyncGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
|
|
|
{
|
|
|
if (source == null)
|
|
@@ -87,6 +111,20 @@ namespace System.Linq
|
|
|
return new GroupedAsyncEnumerableWithTask<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer: null);
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ public static IAsyncEnumerable<IAsyncGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector)
|
|
|
+ {
|
|
|
+ if (source == null)
|
|
|
+ throw Error.ArgumentNull(nameof(source));
|
|
|
+ if (keySelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(keySelector));
|
|
|
+ if (elementSelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(elementSelector));
|
|
|
+
|
|
|
+ return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer: null);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
public static IAsyncEnumerable<IAsyncGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
|
|
|
{
|
|
|
if (source == null)
|
|
@@ -99,6 +137,20 @@ namespace System.Linq
|
|
|
return new GroupedAsyncEnumerableWithTask<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer);
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ public static IAsyncEnumerable<IAsyncGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
|
|
|
+ {
|
|
|
+ if (source == null)
|
|
|
+ throw Error.ArgumentNull(nameof(source));
|
|
|
+ if (keySelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(keySelector));
|
|
|
+ if (elementSelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(elementSelector));
|
|
|
+
|
|
|
+ return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TElement>(source, keySelector, elementSelector, comparer);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IAsyncEnumerable<TSource>, TResult> resultSelector)
|
|
|
{
|
|
|
if (source == null)
|
|
@@ -135,6 +187,20 @@ namespace System.Linq
|
|
|
return new GroupedResultAsyncEnumerableWithTask<TSource, TKey, TResult>(source, keySelector, resultSelector, comparer: null);
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TKey, IAsyncEnumerable<TSource>, CancellationToken, ValueTask<TResult>> resultSelector)
|
|
|
+ {
|
|
|
+ if (source == null)
|
|
|
+ throw Error.ArgumentNull(nameof(source));
|
|
|
+ if (keySelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(keySelector));
|
|
|
+ if (resultSelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(resultSelector));
|
|
|
+
|
|
|
+ return new GroupedResultAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TResult>(source, keySelector, resultSelector, comparer: null);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TKey, IAsyncEnumerable<TSource>, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
|
|
|
{
|
|
|
if (source == null)
|
|
@@ -147,6 +213,20 @@ namespace System.Linq
|
|
|
return new GroupedResultAsyncEnumerableWithTask<TSource, TKey, TResult>(source, keySelector, resultSelector, comparer);
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TKey, IAsyncEnumerable<TSource>, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
|
|
|
+ {
|
|
|
+ if (source == null)
|
|
|
+ throw Error.ArgumentNull(nameof(source));
|
|
|
+ if (keySelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(keySelector));
|
|
|
+ if (resultSelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(resultSelector));
|
|
|
+
|
|
|
+ return new GroupedResultAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TResult>(source, keySelector, resultSelector, comparer);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IAsyncEnumerable<TElement>, TResult> resultSelector)
|
|
|
{
|
|
|
if (source == null)
|
|
@@ -189,6 +269,22 @@ namespace System.Linq
|
|
|
return source.GroupBy<TSource, TKey, TElement>(keySelector, elementSelector, comparer: null).Select(g => resultSelector(g.Key, g));
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, Func<TKey, IAsyncEnumerable<TElement>, CancellationToken, ValueTask<TResult>> resultSelector)
|
|
|
+ {
|
|
|
+ if (source == null)
|
|
|
+ throw Error.ArgumentNull(nameof(source));
|
|
|
+ if (keySelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(keySelector));
|
|
|
+ if (elementSelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(elementSelector));
|
|
|
+ if (resultSelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(resultSelector));
|
|
|
+
|
|
|
+ return source.GroupBy(keySelector, elementSelector, comparer: null).Select((g, ct) => resultSelector(g.Key, g, ct));
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<TKey>> keySelector, Func<TSource, ValueTask<TElement>> elementSelector, Func<TKey, IAsyncEnumerable<TElement>, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
|
|
|
{
|
|
|
if (source == null)
|
|
@@ -203,6 +299,22 @@ namespace System.Linq
|
|
|
return source.GroupBy(keySelector, elementSelector, comparer).Select(g => resultSelector(g.Key, g));
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ public static IAsyncEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, Func<TKey, IAsyncEnumerable<TElement>, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
|
|
|
+ {
|
|
|
+ if (source == null)
|
|
|
+ throw Error.ArgumentNull(nameof(source));
|
|
|
+ if (keySelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(keySelector));
|
|
|
+ if (elementSelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(elementSelector));
|
|
|
+ if (resultSelector == null)
|
|
|
+ throw Error.ArgumentNull(nameof(resultSelector));
|
|
|
+
|
|
|
+ return source.GroupBy(keySelector, elementSelector, comparer).Select((g, ct) => resultSelector(g.Key, g, ct));
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
internal sealed class GroupedResultAsyncEnumerable<TSource, TKey, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
|
|
|
{
|
|
|
private readonly IAsyncEnumerable<TSource> _source;
|
|
@@ -389,6 +501,101 @@ namespace System.Linq
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ internal sealed class GroupedResultAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TResult> : AsyncIterator<TResult>, IAsyncIListProvider<TResult>
|
|
|
+ {
|
|
|
+ private readonly IAsyncEnumerable<TSource> _source;
|
|
|
+ private readonly Func<TSource, CancellationToken, ValueTask<TKey>> _keySelector;
|
|
|
+ private readonly Func<TKey, IAsyncEnumerable<TSource>, CancellationToken, ValueTask<TResult>> _resultSelector;
|
|
|
+ private readonly IEqualityComparer<TKey> _comparer;
|
|
|
+
|
|
|
+ private Internal.LookupWithTask<TKey, TSource> _lookup;
|
|
|
+ private IAsyncEnumerator<TResult> _enumerator;
|
|
|
+
|
|
|
+ public GroupedResultAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TKey, IAsyncEnumerable<TSource>, CancellationToken, ValueTask<TResult>> resultSelector, IEqualityComparer<TKey> comparer)
|
|
|
+ {
|
|
|
+ Debug.Assert(source != null);
|
|
|
+ Debug.Assert(keySelector != null);
|
|
|
+ Debug.Assert(resultSelector != null);
|
|
|
+
|
|
|
+ _source = source;
|
|
|
+ _keySelector = keySelector;
|
|
|
+ _resultSelector = resultSelector;
|
|
|
+ _comparer = comparer;
|
|
|
+ }
|
|
|
+
|
|
|
+ public override AsyncIteratorBase<TResult> Clone()
|
|
|
+ {
|
|
|
+ return new GroupedResultAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TResult>(_source, _keySelector, _resultSelector, _comparer);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override async ValueTask DisposeAsync()
|
|
|
+ {
|
|
|
+ if (_enumerator != null)
|
|
|
+ {
|
|
|
+ await _enumerator.DisposeAsync().ConfigureAwait(false);
|
|
|
+ _enumerator = null;
|
|
|
+ _lookup = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ await base.DisposeAsync().ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override async ValueTask<bool> MoveNextCore()
|
|
|
+ {
|
|
|
+ switch (_state)
|
|
|
+ {
|
|
|
+ case AsyncIteratorState.Allocated:
|
|
|
+ _lookup = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false);
|
|
|
+ _enumerator = _lookup.Select(async g => await _resultSelector(g.Key, g, _cancellationToken).ConfigureAwait(false)).GetAsyncEnumerator(_cancellationToken);
|
|
|
+ _state = AsyncIteratorState.Iterating;
|
|
|
+ goto case AsyncIteratorState.Iterating;
|
|
|
+
|
|
|
+ case AsyncIteratorState.Iterating:
|
|
|
+ if (await _enumerator.MoveNextAsync().ConfigureAwait(false))
|
|
|
+ {
|
|
|
+ _current = _enumerator.Current;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ await DisposeAsync().ConfigureAwait(false);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<TResult[]> ToArrayAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+ return await l.ToArray(_resultSelector, cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<List<TResult>> ToListAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+ return await l.ToList(_resultSelector, cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (onlyIfCheap)
|
|
|
+ {
|
|
|
+ return TaskExt.MinusOne;
|
|
|
+ }
|
|
|
+
|
|
|
+ return Core();
|
|
|
+
|
|
|
+ async Task<int> Core()
|
|
|
+ {
|
|
|
+ var l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+
|
|
|
+ return l.Count;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
internal sealed class GroupedAsyncEnumerable<TSource, TKey, TElement> : AsyncIterator<IAsyncGrouping<TKey, TElement>>, IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>
|
|
|
{
|
|
|
private readonly IAsyncEnumerable<TSource> _source;
|
|
@@ -575,6 +782,101 @@ namespace System.Linq
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ internal sealed class GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TElement> : AsyncIterator<IAsyncGrouping<TKey, TElement>>, IAsyncIListProvider<IAsyncGrouping<TKey, TElement>>
|
|
|
+ {
|
|
|
+ private readonly IAsyncEnumerable<TSource> _source;
|
|
|
+ private readonly Func<TSource, CancellationToken, ValueTask<TKey>> _keySelector;
|
|
|
+ private readonly Func<TSource, CancellationToken, ValueTask<TElement>> _elementSelector;
|
|
|
+ private readonly IEqualityComparer<TKey> _comparer;
|
|
|
+
|
|
|
+ private Internal.LookupWithTask<TKey, TElement> _lookup;
|
|
|
+ private IEnumerator<IGrouping<TKey, TElement>> _enumerator;
|
|
|
+
|
|
|
+ public GroupedAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, Func<TSource, CancellationToken, ValueTask<TElement>> elementSelector, IEqualityComparer<TKey> comparer)
|
|
|
+ {
|
|
|
+ Debug.Assert(source != null);
|
|
|
+ Debug.Assert(keySelector != null);
|
|
|
+ Debug.Assert(elementSelector != null);
|
|
|
+
|
|
|
+ _source = source;
|
|
|
+ _keySelector = keySelector;
|
|
|
+ _elementSelector = elementSelector;
|
|
|
+ _comparer = comparer;
|
|
|
+ }
|
|
|
+
|
|
|
+ public override AsyncIteratorBase<IAsyncGrouping<TKey, TElement>> Clone()
|
|
|
+ {
|
|
|
+ return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey, TElement>(_source, _keySelector, _elementSelector, _comparer);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override async ValueTask DisposeAsync()
|
|
|
+ {
|
|
|
+ if (_enumerator != null)
|
|
|
+ {
|
|
|
+ _enumerator.Dispose();
|
|
|
+ _enumerator = null;
|
|
|
+ _lookup = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ await base.DisposeAsync().ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override async ValueTask<bool> MoveNextCore()
|
|
|
+ {
|
|
|
+ switch (_state)
|
|
|
+ {
|
|
|
+ case AsyncIteratorState.Allocated:
|
|
|
+ _lookup = await Internal.LookupWithTask<TKey, TElement>.CreateAsync(_source, _keySelector, _elementSelector, _comparer, _cancellationToken).ConfigureAwait(false);
|
|
|
+ _enumerator = _lookup.GetEnumerator();
|
|
|
+ _state = AsyncIteratorState.Iterating;
|
|
|
+ goto case AsyncIteratorState.Iterating;
|
|
|
+
|
|
|
+ case AsyncIteratorState.Iterating:
|
|
|
+ if (_enumerator.MoveNext())
|
|
|
+ {
|
|
|
+ _current = (IAsyncGrouping<TKey, TElement>)_enumerator.Current;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ await DisposeAsync().ConfigureAwait(false);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<IAsyncGrouping<TKey, TElement>[]> ToArrayAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ IAsyncIListProvider<IAsyncGrouping<TKey, TElement>> l = await Internal.LookupWithTask<TKey, TElement>.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+ return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<List<IAsyncGrouping<TKey, TElement>>> ToListAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ IAsyncIListProvider<IAsyncGrouping<TKey, TElement>> l = await Internal.LookupWithTask<TKey, TElement>.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+ return await l.ToListAsync(cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (onlyIfCheap)
|
|
|
+ {
|
|
|
+ return TaskExt.MinusOne;
|
|
|
+ }
|
|
|
+
|
|
|
+ return Core();
|
|
|
+
|
|
|
+ async Task<int> Core()
|
|
|
+ {
|
|
|
+ var l = await Internal.LookupWithTask<TKey, TElement>.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+
|
|
|
+ return l.Count;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
internal sealed class GroupedAsyncEnumerable<TSource, TKey> : AsyncIterator<IAsyncGrouping<TKey, TSource>>, IAsyncIListProvider<IAsyncGrouping<TKey, TSource>>
|
|
|
{
|
|
|
private readonly IAsyncEnumerable<TSource> _source;
|
|
@@ -754,5 +1056,97 @@ namespace System.Linq
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+#if !NO_DEEP_CANCELLATION
|
|
|
+ internal sealed class GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey> : AsyncIterator<IAsyncGrouping<TKey, TSource>>, IAsyncIListProvider<IAsyncGrouping<TKey, TSource>>
|
|
|
+ {
|
|
|
+ private readonly IAsyncEnumerable<TSource> _source;
|
|
|
+ private readonly Func<TSource, CancellationToken, ValueTask<TKey>> _keySelector;
|
|
|
+ private readonly IEqualityComparer<TKey> _comparer;
|
|
|
+
|
|
|
+ private Internal.LookupWithTask<TKey, TSource> _lookup;
|
|
|
+ private IEnumerator<IGrouping<TKey, TSource>> _enumerator;
|
|
|
+
|
|
|
+ public GroupedAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<TKey>> keySelector, IEqualityComparer<TKey> comparer)
|
|
|
+ {
|
|
|
+ Debug.Assert(source != null);
|
|
|
+ Debug.Assert(keySelector != null);
|
|
|
+
|
|
|
+ _source = source;
|
|
|
+ _keySelector = keySelector;
|
|
|
+ _comparer = comparer;
|
|
|
+ }
|
|
|
+
|
|
|
+ public override AsyncIteratorBase<IAsyncGrouping<TKey, TSource>> Clone()
|
|
|
+ {
|
|
|
+ return new GroupedAsyncEnumerableWithTaskAndCancellation<TSource, TKey>(_source, _keySelector, _comparer);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override async ValueTask DisposeAsync()
|
|
|
+ {
|
|
|
+ if (_enumerator != null)
|
|
|
+ {
|
|
|
+ _enumerator.Dispose();
|
|
|
+ _enumerator = null;
|
|
|
+ _lookup = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ await base.DisposeAsync().ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override async ValueTask<bool> MoveNextCore()
|
|
|
+ {
|
|
|
+ switch (_state)
|
|
|
+ {
|
|
|
+ case AsyncIteratorState.Allocated:
|
|
|
+ _lookup = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false);
|
|
|
+ _enumerator = _lookup.GetEnumerator();
|
|
|
+ _state = AsyncIteratorState.Iterating;
|
|
|
+ goto case AsyncIteratorState.Iterating;
|
|
|
+
|
|
|
+ case AsyncIteratorState.Iterating:
|
|
|
+ if (_enumerator.MoveNext())
|
|
|
+ {
|
|
|
+ _current = (IAsyncGrouping<TKey, TSource>)_enumerator.Current;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ await DisposeAsync().ConfigureAwait(false);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<IAsyncGrouping<TKey, TSource>[]> ToArrayAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ IAsyncIListProvider<IAsyncGrouping<TKey, TSource>> l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+ return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<List<IAsyncGrouping<TKey, TSource>>> ToListAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ IAsyncIListProvider<IAsyncGrouping<TKey, TSource>> l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+ return await l.ToListAsync(cancellationToken).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ if (onlyIfCheap)
|
|
|
+ {
|
|
|
+ return TaskExt.MinusOne;
|
|
|
+ }
|
|
|
+
|
|
|
+ return Core();
|
|
|
+
|
|
|
+ async Task<int> Core()
|
|
|
+ {
|
|
|
+ var l = await Internal.LookupWithTask<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false);
|
|
|
+
|
|
|
+ return l.Count;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
}
|
|
|
}
|