// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace System.Linq { public static partial class AsyncEnumerable { #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES // The next two methods are replaced by a single method in System.Linq.AsyncEnumerable: // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.groupby?view=net-9.0-pp#system-linq-asyncenumerable-groupby-2(system-collections-generic-iasyncenumerable((-0))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))) // It has a different signature from both: // Returns an IAsyncEnumerable>, which is not the same as IAsyncGrouping // Supplies a default value of null for the comparer // That second difference is why there's only the one overload. // The first difference seems large: IAsyncGrouping returns each group as an IAsyncEnumerable. In practice, // the grouping operators enumerate the source to completion before returning anything so in practice this // async capability is not used, and just complicates things for the consumer. /// /// Groups the elements of an async-enumerable sequence according to a specified key selector function. /// /// The type of the elements in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// An async-enumerable sequence whose elements to group. /// A function to extract the key for each element. /// A sequence of async-enumerable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. /// or is null. public static IAsyncEnumerable> GroupBy(this IAsyncEnumerable source, Func keySelector) => new GroupedAsyncEnumerable(source, keySelector, comparer: null); /// /// Groups the elements of an async-enumerable sequence according to a specified key selector function and comparer. /// /// The type of the elements in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// An async-enumerable sequence whose elements to group. /// A function to extract the key for each element. /// An equality comparer to compare keys with. /// A sequence of async-enumerable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. /// or or is null. public static IAsyncEnumerable> GroupBy(this IAsyncEnumerable source, Func keySelector, IEqualityComparer? comparer) => new GroupedAsyncEnumerable(source, keySelector, comparer); #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES /// /// Groups the elements of an async-enumerable sequence according to a specified key selector function. /// /// The type of the elements in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// An async-enumerable sequence whose elements to group. /// An asynchronous function to extract the key for each element. /// A sequence of async-enumerable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. /// or is . [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwait functionality now exists as overloads of GroupBy. You will need to modify your callback to take an additional CancellationToken argument.")] private static IAsyncEnumerable> GroupByAwaitCore(this IAsyncEnumerable source, Func> keySelector) => new GroupedAsyncEnumerableWithTask(source, keySelector, comparer: null); /// /// Groups the elements of an async-enumerable sequence according to a specified key selector function and comparer. /// /// The type of the elements in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// An async-enumerable sequence whose elements to group. /// An asynchronous function to extract the key for each element. /// An equality comparer to compare keys with. /// A sequence of async-enumerable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. /// or or is . [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwait functionality now exists as overloads of GroupBy. You will need to modify your callback to take an additional CancellationToken argument.")] private static IAsyncEnumerable> GroupByAwaitCore(this IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer) => new GroupedAsyncEnumerableWithTask(source, keySelector, comparer); #if !NO_DEEP_CANCELLATION [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwaitWithCancellationAsync functionality now exists as overloads of GroupBy.")] private static IAsyncEnumerable> GroupByAwaitWithCancellationCore(this IAsyncEnumerable source, Func> keySelector) => new GroupedAsyncEnumerableWithTaskAndCancellation(source, keySelector, comparer: null); [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwaitWithCancellationAsync functionality now exists as overloads of GroupBy.")] private static IAsyncEnumerable> GroupByAwaitWithCancellationCore(this IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer) => new GroupedAsyncEnumerableWithTaskAndCancellation(source, keySelector, comparer); #endif #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES // The next two methods are replaced by a single method in System.Linq.AsyncEnumerable: // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.groupby?view=net-9.0-pp#system-linq-asyncenumerable-groupby-3(system-collections-generic-iasyncenumerable((-0))-system-func((-0-1))-system-func((-0-2))-system-collections-generic-iequalitycomparer((-1))) // It has a different signature from both: // Returns an IAsyncEnumerable>, which is not the same as IAsyncGrouping // Supplies a default value of null for the comparer // That second difference is why there's only the one overload. // The first difference seems large: IAsyncGrouping returns each group as an IAsyncEnumerable. In practice, // the grouping operators enumerate the source to completion before returning anything so in practice this // async capability is not used, and just complicates things for the consumer. /// /// Groups the elements of an async-enumerable sequence and selects the resulting elements by using a specified function. /// /// The type of the elements in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// The type of the elements within the groups computed for each element in the source sequence. /// An async-enumerable sequence whose elements to group. /// A function to extract the key for each element. /// A function to map each source element to an element in an async-enumerable group. /// A sequence of async-enumerable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. /// or or is null. public static IAsyncEnumerable> GroupBy(this IAsyncEnumerable source, Func keySelector, Func elementSelector) => new GroupedAsyncEnumerable(source, keySelector, elementSelector, comparer: null); /// /// Groups the elements of an async-enumerable sequence according to a specified key selector function and comparer and selects the resulting elements by using a specified function. /// /// The type of the elements in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// The type of the elements within the groups computed for each element in the source sequence. /// An async-enumerable sequence whose elements to group. /// A function to extract the key for each element. /// A function to map each source element to an element in an async-enumerable group. /// An equality comparer to compare keys with. /// A sequence of async-enumerable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. /// or or or is null. public static IAsyncEnumerable> GroupBy(this IAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer? comparer) => new GroupedAsyncEnumerable(source, keySelector, elementSelector, comparer); #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES /// /// Groups the elements of an async-enumerable sequence and selects the resulting elements by using a specified function. /// /// The type of the elements in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// The type of the elements within the groups computed for each element in the source sequence. /// An async-enumerable sequence whose elements to group. /// An asynchronous function to extract the key for each element. /// An asynchronous function to map each source element to an element in an async-enumerable group. /// A sequence of async-enumerable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. /// or or is . [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwait functionality now exists as overloads of GroupBy. You will need to modify your callback to take an additional CancellationToken argument.")] private static IAsyncEnumerable> GroupByAwaitCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector) => new GroupedAsyncEnumerableWithTask(source, keySelector, elementSelector, comparer: null); /// /// Groups the elements of an async-enumerable sequence and selects the resulting elements by using a specified function. /// /// The type of the elements in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// The type of the elements within the groups computed for each element in the source sequence. /// An async-enumerable sequence whose elements to group. /// An asynchronous function to extract the key for each element. /// An asynchronous function to map each source element to an element in an async-enumerable group. /// An equality comparer to use to compare keys. /// A sequence of async-enumerable groups, each of which corresponds to a unique key value, containing all elements that share that same key value. /// or or or is . [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwait functionality now exists as overloads of GroupBy. You will need to modify your callback to take an additional CancellationToken argument.")] private static IAsyncEnumerable> GroupByAwaitCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer? comparer) => new GroupedAsyncEnumerableWithTask(source, keySelector, elementSelector, comparer); #if !NO_DEEP_CANCELLATION [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwaitWithCancellationAsync functionality now exists as overloads of GroupBy.")] private static IAsyncEnumerable> GroupByAwaitWithCancellationCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector) => new GroupedAsyncEnumerableWithTaskAndCancellation(source, keySelector, elementSelector, comparer: null); [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwaitWithCancellationAsync functionality now exists as overloads of GroupBy.")] private static IAsyncEnumerable> GroupByAwaitWithCancellationCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer? comparer) => new GroupedAsyncEnumerableWithTaskAndCancellation(source, keySelector, elementSelector, comparer); #endif #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.groupby?view=net-9.0-pp#system-linq-asyncenumerable-groupby-3(system-collections-generic-iasyncenumerable((-0))-system-func((-0-1))-system-func((-1-system-collections-generic-ienumerable((-0))-2))-system-collections-generic-iequalitycomparer((-1))) public static IAsyncEnumerable GroupBy(this IAsyncEnumerable source, Func keySelector, Func, TResult> resultSelector) => new GroupedResultAsyncEnumerable(source, keySelector, resultSelector, comparer: null); public static IAsyncEnumerable GroupBy(this IAsyncEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer? comparer) => new GroupedResultAsyncEnumerable(source, keySelector, resultSelector, comparer); #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES /// /// Groups the elements of an async-enumerable sequence according to a specified key selector function, and then applies a result selector function to each group. /// /// Type of element in the source sequence. /// Type of the grouping key computed for each element in the source sequence. /// The result type returned by the result selector function. /// An async-enumerable sequence whose elements to group. /// An asynchronous function to extract the key for each element. /// An asynchronous function to transform each group into the result type. /// An async-enumerable sequence of results obtained by invoking and awaiting the result-selector function on each group. /// or or is . [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwait functionality now exists as overloads of GroupBy. You will need to modify your callback to take an additional CancellationToken argument.")] private static IAsyncEnumerable GroupByAwaitCore(this IAsyncEnumerable source, Func> keySelector, Func, ValueTask> resultSelector) => new GroupedResultAsyncEnumerableWithTask(source, keySelector, resultSelector, comparer: null); /// /// Groups the elements of an async-enumerable sequence according to a specified key selector function, and then applies a result selector function to each group. /// /// Type of element in the source sequence. /// Type of the grouping key computed for each element in the source sequence. /// The result type returned by the result selector function. /// An async-enumerable sequence whose elements to group. /// An asynchronous function to extract the key for each element. /// An asynchronous function to transform each group into the result type. /// An equality comparer to use to compare keys. /// An async-enumerable sequence of results obtained by invoking and awaiting the result-selector function on each group. /// or or or is . [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwait functionality now exists as overloads of GroupBy. You will need to modify your callback to take an additional CancellationToken argument.")] private static IAsyncEnumerable GroupByAwaitCore(this IAsyncEnumerable source, Func> keySelector, Func, ValueTask> resultSelector, IEqualityComparer? comparer) => new GroupedResultAsyncEnumerableWithTask(source, keySelector, resultSelector, comparer); #if !NO_DEEP_CANCELLATION [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwaitWithCancellationAsync functionality now exists as overloads of GroupBy.")] private static IAsyncEnumerable GroupByAwaitWithCancellationCore(this IAsyncEnumerable source, Func> keySelector, Func, CancellationToken, ValueTask> resultSelector) => new GroupedResultAsyncEnumerableWithTaskAndCancellation(source, keySelector, resultSelector, comparer: null); [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwaitWithCancellationAsync functionality now exists as overloads of GroupBy.")] private static IAsyncEnumerable GroupByAwaitWithCancellationCore(this IAsyncEnumerable source, Func> keySelector, Func, CancellationToken, ValueTask> resultSelector, IEqualityComparer? comparer) => new GroupedResultAsyncEnumerableWithTaskAndCancellation(source, keySelector, resultSelector, comparer); #endif #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES // This covers the next two // https://learn.microsoft.com/en-us/dotnet/api/system.linq.asyncenumerable.groupby?view=net-9.0-pp#system-linq-asyncenumerable-groupby-4(system-collections-generic-iasyncenumerable((-0))-system-func((-0-system-threading-cancellationtoken-system-threading-tasks-valuetask((-1))))-system-func((-0-system-threading-cancellationtoken-system-threading-tasks-valuetask((-2))))-system-func((-1-system-collections-generic-ienumerable((-2))-system-threading-cancellationtoken-system-threading-tasks-valuetask((-3))))-system-collections-generic-iequalitycomparer((-1))) public static IAsyncEnumerable GroupBy(this IAsyncEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector) => new GroupedResultAsyncEnumerable(source, keySelector, elementSelector, resultSelector, comparer: null); public static IAsyncEnumerable GroupBy(this IAsyncEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector, IEqualityComparer? comparer) => new GroupedResultAsyncEnumerable(source, keySelector, elementSelector, resultSelector, comparer); #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES /// /// Groups the elements of an async-enumerable sequence according to a specified key-selector function, applies an element selector to each element of each group, then applies a result selector to each transformed group. /// /// The type of element in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// The type of element computed by the element selector. /// The type of the final result, computed by applying the result selector to each transformed group of elements. /// An async-enumerable sequence whose elements to group. /// An asynchronous function to extract the key for each element. /// An asynchronous function to apply to each element of each group. /// An asynchronous function to transform each group into the result type. /// An async-enumerable sequence of results obtained by invoking the result selector function on each group and awaiting the result. /// or or or is . [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwait functionality now exists as overloads of GroupBy. You will need to modify your callback to take an additional CancellationToken argument.")] private static IAsyncEnumerable GroupByAwaitCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, Func, ValueTask> resultSelector) => new GroupedResultAsyncEnumerableWithTask(source, keySelector, elementSelector, resultSelector, comparer: null); /// /// Groups the elements of an async-enumerable sequence according to a specified key-selector function, applies an element selector to each element of each group, then applies a result selector to each transformed group. /// /// The type of element in the source sequence. /// The type of the grouping key computed for each element in the source sequence. /// The type of element computed by the element selector. /// The type of the final result, computed by applying the result selector to each transformed group of elements. /// An async-enumerable sequence whose elements to group. /// An asynchronous function to extract the key for each element. /// An asynchronous function to apply to each element of each group. /// An asynchronous function to transform each group into the result type. /// An equality comparer to use to compare keys. /// An async-enumerable sequence of results obtained by invoking the result selector function on each group and awaiting the result. /// or or or or is . [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwait functionality now exists as overloads of GroupBy. You will need to modify your callback to take an additional CancellationToken argument.")] private static IAsyncEnumerable GroupByAwaitCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, Func, ValueTask> resultSelector, IEqualityComparer? comparer) => new GroupedResultAsyncEnumerableWithTask(source, keySelector, elementSelector, resultSelector, comparer); #if !NO_DEEP_CANCELLATION [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwaitWithCancellationAsync functionality now exists as overloads of GroupBy.")] private static IAsyncEnumerable GroupByAwaitWithCancellationCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, Func, CancellationToken, ValueTask> resultSelector) => new GroupedResultAsyncEnumerableWithTaskAndCancellation(source, keySelector, elementSelector, resultSelector, comparer: null); [GenerateAsyncOverload] [Obsolete("Use GroupBy. IAsyncEnumerable LINQ is now in System.Linq.AsyncEnumerable, and the GroupByAwaitWithCancellationAsync functionality now exists as overloads of GroupBy.")] private static IAsyncEnumerable GroupByAwaitWithCancellationCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, Func, CancellationToken, ValueTask> resultSelector, IEqualityComparer? comparer) => new GroupedResultAsyncEnumerableWithTaskAndCancellation(source, keySelector, elementSelector, resultSelector, comparer); #endif private sealed class GroupedResultAsyncEnumerable : AsyncIterator, IAsyncIListProvider { private readonly IAsyncEnumerable _source; private readonly Func _keySelector; private readonly Func, TResult> _resultSelector; private readonly IEqualityComparer? _comparer; private Internal.Lookup? _lookup; private IEnumerator? _enumerator; public GroupedResultAsyncEnumerable(IAsyncEnumerable source, Func keySelector, Func, TResult> resultSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _resultSelector = resultSelector ?? throw Error.ArgumentNull(nameof(resultSelector)); _comparer = comparer; } public override AsyncIteratorBase Clone() { return new GroupedResultAsyncEnumerable(_source, _keySelector, _resultSelector, _comparer); } public override async ValueTask DisposeAsync() { if (_enumerator != null) { _enumerator.Dispose(); _enumerator = null; _lookup = null; } await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.Lookup.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false); _enumerator = _lookup.ApplyResultSelector(_resultSelector).GetEnumerator(); _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: if (_enumerator!.MoveNext()) { _current = _enumerator.Current; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public async ValueTask ToArrayAsync(CancellationToken cancellationToken) { var l = await Internal.Lookup.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return l.ToArray(_resultSelector); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var l = await Internal.Lookup.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return l.ToList(_resultSelector); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.Lookup.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } private sealed class GroupedResultAsyncEnumerableWithTask : AsyncIterator, IAsyncIListProvider { private readonly IAsyncEnumerable _source; private readonly Func> _keySelector; private readonly Func, ValueTask> _resultSelector; private readonly IEqualityComparer? _comparer; private Internal.LookupWithTask? _lookup; private IAsyncEnumerator? _enumerator; public GroupedResultAsyncEnumerableWithTask(IAsyncEnumerable source, Func> keySelector, Func, ValueTask> resultSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _resultSelector = resultSelector ?? throw Error.ArgumentNull(nameof(resultSelector)); _comparer = comparer; } public override AsyncIteratorBase Clone() { return new GroupedResultAsyncEnumerableWithTask(_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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false); _enumerator = _lookup.SelectAwaitCore(async g => await _resultSelector(g.Key, g).ConfigureAwait(false)).GetAsyncEnumerator(_cancellationToken); // REVIEW: Introduce another ApplyResultSelector? _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 ValueTask ToArrayAsync(CancellationToken cancellationToken) { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArray(_resultSelector).ConfigureAwait(false); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToList(_resultSelector).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } #if !NO_DEEP_CANCELLATION private sealed class GroupedResultAsyncEnumerableWithTaskAndCancellation : AsyncIterator, IAsyncIListProvider { private readonly IAsyncEnumerable _source; private readonly Func> _keySelector; private readonly Func, CancellationToken, ValueTask> _resultSelector; private readonly IEqualityComparer? _comparer; private Internal.LookupWithTask? _lookup; private IAsyncEnumerator? _enumerator; public GroupedResultAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable source, Func> keySelector, Func, CancellationToken, ValueTask> resultSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _resultSelector = resultSelector ?? throw Error.ArgumentNull(nameof(resultSelector)); _comparer = comparer; } public override AsyncIteratorBase Clone() { return new GroupedResultAsyncEnumerableWithTaskAndCancellation(_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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false); _enumerator = _lookup.SelectAwaitCore(async g => await _resultSelector(g.Key, g, _cancellationToken).ConfigureAwait(false)).GetAsyncEnumerator(_cancellationToken); // REVIEW: Introduce another ApplyResultSelector? _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 ValueTask ToArrayAsync(CancellationToken cancellationToken) { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArray(_resultSelector, cancellationToken).ConfigureAwait(false); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToList(_resultSelector, cancellationToken).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } #endif private sealed class GroupedResultAsyncEnumerable : AsyncIterator, IAsyncIListProvider { private readonly IAsyncEnumerable _source; private readonly Func _keySelector; private readonly Func _elementSelector; private readonly Func, TResult> _resultSelector; private readonly IEqualityComparer? _comparer; private Internal.Lookup? _lookup; private IEnumerator? _enumerator; public GroupedResultAsyncEnumerable(IAsyncEnumerable source, Func keySelector, Func elementSelector, Func, TResult> resultSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _elementSelector = elementSelector ?? throw Error.ArgumentNull(nameof(elementSelector)); _resultSelector = resultSelector ?? throw Error.ArgumentNull(nameof(resultSelector)); _comparer = comparer; } public override AsyncIteratorBase Clone() { return new GroupedResultAsyncEnumerable(_source, _keySelector, _elementSelector, _resultSelector, _comparer); } public override async ValueTask DisposeAsync() { if (_enumerator != null) { _enumerator.Dispose(); _enumerator = null; _lookup = null; } await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.Lookup.CreateAsync(_source, _keySelector, _elementSelector, _comparer, _cancellationToken).ConfigureAwait(false); _enumerator = _lookup.ApplyResultSelector(_resultSelector).GetEnumerator(); _state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: if (_enumerator!.MoveNext()) { _current = _enumerator.Current; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public async ValueTask ToArrayAsync(CancellationToken cancellationToken) { var l = await Internal.Lookup.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return l.ToArray(_resultSelector); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var l = await Internal.Lookup.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return l.ToList(_resultSelector); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.Lookup.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } private sealed class GroupedResultAsyncEnumerableWithTask : AsyncIterator, IAsyncIListProvider { private readonly IAsyncEnumerable _source; private readonly Func> _keySelector; private readonly Func> _elementSelector; private readonly Func, ValueTask> _resultSelector; private readonly IEqualityComparer? _comparer; private Internal.LookupWithTask? _lookup; private IAsyncEnumerator? _enumerator; public GroupedResultAsyncEnumerableWithTask(IAsyncEnumerable source, Func> keySelector, Func> elementSelector, Func, ValueTask> resultSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _elementSelector = elementSelector ?? throw Error.ArgumentNull(nameof(elementSelector)); _resultSelector = resultSelector ?? throw Error.ArgumentNull(nameof(resultSelector)); _comparer = comparer; } public override AsyncIteratorBase Clone() { return new GroupedResultAsyncEnumerableWithTask(_source, _keySelector, _elementSelector, _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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, _cancellationToken).ConfigureAwait(false); _enumerator = _lookup.SelectAwaitCore(async g => await _resultSelector(g.Key, g).ConfigureAwait(false)).GetAsyncEnumerator(_cancellationToken); // REVIEW: Introduce another ApplyResultSelector? _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 ValueTask ToArrayAsync(CancellationToken cancellationToken) { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArray(_resultSelector).ConfigureAwait(false); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToList(_resultSelector).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } #if !NO_DEEP_CANCELLATION private sealed class GroupedResultAsyncEnumerableWithTaskAndCancellation : AsyncIterator, IAsyncIListProvider { private readonly IAsyncEnumerable _source; private readonly Func> _keySelector; private readonly Func> _elementSelector; private readonly Func, CancellationToken, ValueTask> _resultSelector; private readonly IEqualityComparer? _comparer; private Internal.LookupWithTask? _lookup; private IAsyncEnumerator? _enumerator; public GroupedResultAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable source, Func> keySelector, Func> elementSelector, Func, CancellationToken, ValueTask> resultSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _elementSelector = elementSelector ?? throw Error.ArgumentNull(nameof(elementSelector)); _resultSelector = resultSelector ?? throw Error.ArgumentNull(nameof(resultSelector)); _comparer = comparer; } public override AsyncIteratorBase Clone() { return new GroupedResultAsyncEnumerableWithTaskAndCancellation(_source, _keySelector, _elementSelector, _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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, _cancellationToken).ConfigureAwait(false); _enumerator = _lookup.SelectAwaitCore(async g => await _resultSelector(g.Key, g, _cancellationToken).ConfigureAwait(false)).GetAsyncEnumerator(_cancellationToken); // REVIEW: Introduce another ApplyResultSelector? _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 ValueTask ToArrayAsync(CancellationToken cancellationToken) { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArray(_resultSelector, cancellationToken).ConfigureAwait(false); } public async ValueTask> ToListAsync(CancellationToken cancellationToken) { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToList(_resultSelector, cancellationToken).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } #endif private sealed class GroupedAsyncEnumerable : AsyncIterator>, IAsyncIListProvider> { private readonly IAsyncEnumerable _source; private readonly Func _keySelector; private readonly Func _elementSelector; private readonly IEqualityComparer? _comparer; private Internal.Lookup? _lookup; private IEnumerator>? _enumerator; public GroupedAsyncEnumerable(IAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _elementSelector = elementSelector ?? throw Error.ArgumentNull(nameof(elementSelector)); _comparer = comparer; } public override AsyncIteratorBase> Clone() { return new GroupedAsyncEnumerable(_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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.Lookup.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)_enumerator.Current; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public async ValueTask[]> ToArrayAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.Lookup.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false); } public async ValueTask>> ToListAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.Lookup.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToListAsync(cancellationToken).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.Lookup.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } private sealed class GroupedAsyncEnumerableWithTask : AsyncIterator>, IAsyncIListProvider> { private readonly IAsyncEnumerable _source; private readonly Func> _keySelector; private readonly Func> _elementSelector; private readonly IEqualityComparer? _comparer; private Internal.LookupWithTask? _lookup; private IEnumerator>? _enumerator; public GroupedAsyncEnumerableWithTask(IAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _elementSelector = elementSelector ?? throw Error.ArgumentNull(nameof(elementSelector)); _comparer = comparer; } public override AsyncIteratorBase> Clone() { return new GroupedAsyncEnumerableWithTask(_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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.LookupWithTask.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)_enumerator.Current; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public async ValueTask[]> ToArrayAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false); } public async ValueTask>> ToListAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToListAsync(cancellationToken).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } #if !NO_DEEP_CANCELLATION private sealed class GroupedAsyncEnumerableWithTaskAndCancellation : AsyncIterator>, IAsyncIListProvider> { private readonly IAsyncEnumerable _source; private readonly Func> _keySelector; private readonly Func> _elementSelector; private readonly IEqualityComparer? _comparer; private Internal.LookupWithTask? _lookup; private IEnumerator>? _enumerator; public GroupedAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _elementSelector = elementSelector ?? throw Error.ArgumentNull(nameof(elementSelector)); _comparer = comparer; } public override AsyncIteratorBase> Clone() { return new GroupedAsyncEnumerableWithTaskAndCancellation(_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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.LookupWithTask.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)_enumerator.Current; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public async ValueTask[]> ToArrayAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false); } public async ValueTask>> ToListAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToListAsync(cancellationToken).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _elementSelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } #endif private sealed class GroupedAsyncEnumerable : AsyncIterator>, IAsyncIListProvider> { private readonly IAsyncEnumerable _source; private readonly Func _keySelector; private readonly IEqualityComparer? _comparer; private Internal.Lookup? _lookup; private IEnumerator>? _enumerator; public GroupedAsyncEnumerable(IAsyncEnumerable source, Func keySelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _comparer = comparer; } public override AsyncIteratorBase> Clone() { return new GroupedAsyncEnumerable(_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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.Lookup.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)_enumerator.Current; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public async ValueTask[]> ToArrayAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.Lookup.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false); } public async ValueTask>> ToListAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.Lookup.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToListAsync(cancellationToken).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.Lookup.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } private sealed class GroupedAsyncEnumerableWithTask : AsyncIterator>, IAsyncIListProvider> { private readonly IAsyncEnumerable _source; private readonly Func> _keySelector; private readonly IEqualityComparer? _comparer; private Internal.LookupWithTask? _lookup; private IEnumerator>? _enumerator; public GroupedAsyncEnumerableWithTask(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _comparer = comparer; } public override AsyncIteratorBase> Clone() { return new GroupedAsyncEnumerableWithTask(_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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.LookupWithTask.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)_enumerator.Current; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public async ValueTask[]> ToArrayAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false); } public async ValueTask>> ToListAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToListAsync(cancellationToken).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } #if !NO_DEEP_CANCELLATION private sealed class GroupedAsyncEnumerableWithTaskAndCancellation : AsyncIterator>, IAsyncIListProvider> { private readonly IAsyncEnumerable _source; private readonly Func> _keySelector; private readonly IEqualityComparer? _comparer; private Internal.LookupWithTask? _lookup; private IEnumerator>? _enumerator; public GroupedAsyncEnumerableWithTaskAndCancellation(IAsyncEnumerable source, Func> keySelector, IEqualityComparer? comparer) { _source = source ?? throw Error.ArgumentNull(nameof(source)); _keySelector = keySelector ?? throw Error.ArgumentNull(nameof(keySelector)); _comparer = comparer; } public override AsyncIteratorBase> Clone() { return new GroupedAsyncEnumerableWithTaskAndCancellation(_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 MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _lookup = await Internal.LookupWithTask.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)_enumerator.Current; return true; } await DisposeAsync().ConfigureAwait(false); break; } return false; } public async ValueTask[]> ToArrayAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToArrayAsync(cancellationToken).ConfigureAwait(false); } public async ValueTask>> ToListAsync(CancellationToken cancellationToken) { IAsyncIListProvider> l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return await l.ToListAsync(cancellationToken).ConfigureAwait(false); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { if (onlyIfCheap) { return new ValueTask(-1); } return Core(); async ValueTask Core() { var l = await Internal.LookupWithTask.CreateAsync(_source, _keySelector, _comparer, cancellationToken).ConfigureAwait(false); return l.Count; } } } #endif } }