// 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; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace System.Linq { #if INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES public static partial class AsyncEnumerable { /// /// Converts an enumerable sequence to an async-enumerable sequence. /// /// The type of the elements in the source sequence. /// Enumerable sequence to convert to an async-enumerable sequence. /// The async-enumerable sequence whose elements are pulled from the given enumerable sequence. /// is null. public static IAsyncEnumerable ToAsyncEnumerable(this IEnumerable source) { if (source == null) throw Error.ArgumentNull(nameof(source)); return source switch { IList list => new AsyncIListEnumerableAdapter(list), ICollection collection => new AsyncICollectionEnumerableAdapter(collection), _ => new AsyncEnumerableAdapter(source), }; } private sealed class AsyncEnumerableAdapter : AsyncIterator, IAsyncIListProvider { private readonly IEnumerable _source; private IEnumerator? _enumerator; public AsyncEnumerableAdapter(IEnumerable source) { _source = source; } public override AsyncIteratorBase Clone() => new AsyncEnumerableAdapter(_source); public override async ValueTask DisposeAsync() { if (_enumerator != null) { _enumerator.Dispose(); _enumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _enumerator = _source.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; } // // NB: These optimizations rely on the System.Linq implementation of IEnumerable operators to optimize // and short-circuit as appropriate. // public ValueTask ToArrayAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask(_source.ToArray()); } public ValueTask> ToListAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask>(_source.ToList()); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask(_source.Count()); } } private sealed class AsyncIListEnumerableAdapter : AsyncIterator, IAsyncIListProvider, IList { private readonly IList _source; private IEnumerator? _enumerator; public AsyncIListEnumerableAdapter(IList source) { _source = source; } public override AsyncIteratorBase Clone() => new AsyncIListEnumerableAdapter(_source); public override async ValueTask DisposeAsync() { if (_enumerator != null) { _enumerator.Dispose(); _enumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _enumerator = _source.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 override IAsyncEnumerable Select(Func selector) => new SelectIListIterator(_source, selector); // // NB: These optimizations rely on the System.Linq implementation of IEnumerable operators to optimize // and short-circuit as appropriate. // public ValueTask ToArrayAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask(_source.ToArray()); } public ValueTask> ToListAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask>(_source.ToList()); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask(_source.Count); } IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator(); void ICollection.Add(T item) => _source.Add(item); void ICollection.Clear() => _source.Clear(); bool ICollection.Contains(T item) => _source.Contains(item); void ICollection.CopyTo(T[] array, int arrayIndex) => _source.CopyTo(array, arrayIndex); bool ICollection.Remove(T item) => _source.Remove(item); int ICollection.Count => _source.Count; bool ICollection.IsReadOnly => _source.IsReadOnly; int IList.IndexOf(T item) => _source.IndexOf(item); void IList.Insert(int index, T item) => _source.Insert(index, item); void IList.RemoveAt(int index) => _source.RemoveAt(index); T IList.this[int index] { get => _source[index]; set => _source[index] = value; } } private sealed class AsyncICollectionEnumerableAdapter : AsyncIterator, IAsyncIListProvider, ICollection { private readonly ICollection _source; private IEnumerator? _enumerator; public AsyncICollectionEnumerableAdapter(ICollection source) { _source = source; } public override AsyncIteratorBase Clone() => new AsyncICollectionEnumerableAdapter(_source); public override async ValueTask DisposeAsync() { if (_enumerator != null) { _enumerator.Dispose(); _enumerator = null; } await base.DisposeAsync().ConfigureAwait(false); } protected override async ValueTask MoveNextCore() { switch (_state) { case AsyncIteratorState.Allocated: _enumerator = _source.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; } // // NB: These optimizations rely on the System.Linq implementation of IEnumerable operators to optimize // and short-circuit as appropriate. // public ValueTask ToArrayAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask(_source.ToArray()); } public ValueTask> ToListAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask>(_source.ToList()); } public ValueTask GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); return new ValueTask(_source.Count); } IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator(); void ICollection.Add(T item) => _source.Add(item); void ICollection.Clear() => _source.Clear(); bool ICollection.Contains(T item) => _source.Contains(item); void ICollection.CopyTo(T[] array, int arrayIndex) => _source.CopyTo(array, arrayIndex); bool ICollection.Remove(T item) => _source.Remove(item); int ICollection.Count => _source.Count; bool ICollection.IsReadOnly => _source.IsReadOnly; } } #endif // INCLUDE_SYSTEM_LINQ_ASYNCENUMERABLE_DUPLICATES }