// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace System.Linq { public static partial class AsyncEnumerable { public static IAsyncEnumerable CreateEnumerable(Func> getEnumerator) { return new AnonymousAsyncEnumerable(getEnumerator); } public static IAsyncEnumerator CreateEnumerator(Func> moveNext, Func current, Action dispose) { return new AnonymousAsyncIterator(moveNext, current, dispose); } private static IAsyncEnumerator CreateEnumerator(Func, Task> moveNext, Func current, Action dispose) { var self = new AnonymousAsyncIterator( async ct => { var tcs = new TaskCompletionSource(); var stop = new Action( () => { tcs.TrySetCanceled(); }); using (ct.Register(stop)) { return await moveNext(ct, tcs) .ConfigureAwait(false); } }, current, dispose ); return self; } private class AnonymousAsyncEnumerable : IAsyncEnumerable { private readonly Func> getEnumerator; public AnonymousAsyncEnumerable(Func> getEnumerator) { this.getEnumerator = getEnumerator; } public IAsyncEnumerator GetEnumerator() { return getEnumerator(); } } private sealed class AnonymousAsyncIterator : AsyncIterator { private readonly Func currentFunc; private readonly Action dispose; private readonly Func> moveNext; public AnonymousAsyncIterator(Func> moveNext, Func currentFunc, Action dispose) { Debug.Assert(moveNext != null); Debug.Assert(currentFunc != null); Debug.Assert(dispose != null); this.moveNext = moveNext; this.currentFunc = currentFunc; this.dispose = dispose; // Explicit call to initialize enumerator mode GetEnumerator(); } public override AsyncIterator Clone() { throw new NotSupportedException("AnonymousAsyncIterator cannot be cloned. It is only intended for use as an iterator."); } public override void Dispose() { dispose?.Invoke(); base.Dispose(); } protected override async Task MoveNextCore(CancellationToken cancellationToken) { switch (state) { case AsyncIteratorState.Allocated: state = AsyncIteratorState.Iterating; goto case AsyncIteratorState.Iterating; case AsyncIteratorState.Iterating: if (await moveNext(cancellationToken).ConfigureAwait(false)) { current = currentFunc(); return true; } Dispose(); break; } return false; } } } }