// 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
}