Browse Source

Getting rid of USE_AWAIT_USING to ensure proper binding for ConfigureAwait(false).

Bart De Smet 6 years ago
parent
commit
f5fc1e4353
32 changed files with 714 additions and 2980 deletions
  1. 1 1
      Ix.NET/Source/Directory.build.targets
  2. 21 9
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Amb.cs
  3. 28 4
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Catch.cs
  4. 28 4
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/DistinctUntilChanged.cs
  5. 21 3
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Do.cs
  6. 7 1
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/IsEmpty.cs
  7. 4 26
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Max.cs
  8. 4 26
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Min.cs
  9. 12 102
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/MinBy.cs
  10. 21 3
      Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Scan.cs
  11. 4 54
      Ix.NET/Source/System.Linq.Async/System/Linq/AsyncEnumerableHelpers.cs
  12. 12 69
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Aggregate.cs
  13. 4 11
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Any.cs
  14. 84 544
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.Generated.cs
  15. 32 240
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.Generated.tt
  16. 16 71
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/FirstOrDefault.cs
  17. 21 3
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/GroupJoin.cs
  18. 21 3
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Join.cs
  19. 32 212
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Max.cs
  20. 32 212
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Min.cs
  21. 141 592
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.cs
  22. 16 462
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.tt
  23. 10 66
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/OrderedAsyncEnumerable.cs
  24. 8 26
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SequenceEqual.cs
  25. 16 103
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Single.cs
  26. 16 102
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SingleOrDefault.cs
  27. 7 1
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SkipLast.cs
  28. 42 6
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SkipWhile.cs
  29. 7 1
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/TakeLast.cs
  30. 42 6
      Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Zip.cs
  31. 1 4
      Ix.NET/Source/System.Linq.Async/System/Runtime/CompilerServices/ConfiguredCancelableAsyncEnumerable.cs
  32. 3 13
      Ix.NET/Source/System.Linq.Async/System/Threading/Tasks/AsyncEnumerableExtensions.cs

+ 1 - 1
Ix.NET/Source/Directory.build.targets

@@ -22,7 +22,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <DefineConstants>$(DefineConstants);USE_AWAIT_FOREACH;USE_AWAIT_USING</DefineConstants>
+    <DefineConstants>$(DefineConstants);USE_AWAIT_FOREACH</DefineConstants>
   </PropertyGroup>
 
   <PropertyGroup>

+ 21 - 9
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Amb.cs

@@ -95,20 +95,24 @@ namespace System.Linq
 
                 try
                 {
-                    await using (var e = winner.ConfigureAwait(false))
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await moveNextWinner.ConfigureAwait(false))
                         {
                             yield break;
                         }
 
-                        yield return e.Current;
+                        yield return winner.Current;
 
-                        while (await e.MoveNextAsync())
+                        while (await winner.MoveNextAsync().ConfigureAwait(false))
                         {
-                            yield return e.Current;
+                            yield return winner.Current;
                         }
                     }
+                    finally
+                    {
+                        await winner.DisposeAsync().ConfigureAwait(false);
+                    }
                 }
                 finally
                 {
@@ -204,20 +208,24 @@ namespace System.Linq
 
                 try
                 {
-                    await using (var e = winner.ConfigureAwait(false))
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await moveNextWinner.ConfigureAwait(false))
                         {
                             yield break;
                         }
 
-                        yield return e.Current;
+                        yield return winner.Current;
 
-                        while (await e.MoveNextAsync())
+                        while (await winner.MoveNextAsync().ConfigureAwait(false))
                         {
-                            yield return e.Current;
+                            yield return winner.Current;
                         }
                     }
+                    finally
+                    {
+                        await winner.DisposeAsync().ConfigureAwait(false);
+                    }
                 }
                 finally
                 {
@@ -246,13 +254,17 @@ namespace System.Linq
         {
             if (enumerator != null)
             {
-                await using (enumerator.ConfigureAwait(false))
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (moveNextAsync != null)
                     {
                         await moveNextAsync.ConfigureAwait(false);
                     }
                 }
+                finally
+                {
+                    await enumerator.DisposeAsync().ConfigureAwait(false);
+                }
             }
         }
 #endif

+ 28 - 4
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Catch.cs

@@ -39,7 +39,9 @@ namespace System.Linq
 
                 var err = default(IAsyncEnumerable<TSource>);
 
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (true)
                     {
@@ -61,6 +63,10 @@ namespace System.Linq
                         yield return c;
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
 
                 if (err != null)
                 {
@@ -97,7 +103,9 @@ namespace System.Linq
 
                 var err = default(IAsyncEnumerable<TSource>);
 
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (true)
                     {
@@ -119,6 +127,10 @@ namespace System.Linq
                         yield return c;
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
 
                 if (err != null)
                 {
@@ -156,7 +168,9 @@ namespace System.Linq
 
                 var err = default(IAsyncEnumerable<TSource>);
 
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (true)
                     {
@@ -178,6 +192,10 @@ namespace System.Linq
                         yield return c;
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
 
                 if (err != null)
                 {
@@ -230,7 +248,9 @@ namespace System.Linq
 
                 foreach (var source in sources)
                 {
-                    await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                    var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         error = null;
 
@@ -257,6 +277,10 @@ namespace System.Linq
                         if (error == null)
                             break;
                     }
+                    finally
+                    {
+                        await e.DisposeAsync();
+                    }
                 }
 
                 error?.Throw();

+ 28 - 4
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/DistinctUntilChanged.cs

@@ -103,7 +103,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -126,6 +128,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new DistinctUntilChangedAsyncIterator<TSource>(source, comparer);
@@ -144,7 +150,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -171,6 +179,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new DistinctUntilChangedAsyncIterator<TSource, TKey>(source, keySelector, comparer);
@@ -189,7 +201,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -216,6 +230,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new DistinctUntilChangedAsyncIteratorWithTask<TSource, TKey>(source, keySelector, comparer);
@@ -235,7 +253,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -262,6 +282,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new DistinctUntilChangedAsyncIteratorWithTaskAndCancellation<TSource, TKey>(source, keySelector, comparer);

+ 21 - 3
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Do.cs

@@ -176,7 +176,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (true)
                     {
@@ -208,6 +210,10 @@ namespace System.Linq
 
                     onCompleted?.Invoke();
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new DoAsyncIterator<TSource>(source, onNext, onError, onCompleted);
@@ -221,7 +227,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (true)
                     {
@@ -256,6 +264,10 @@ namespace System.Linq
                         await onCompleted().ConfigureAwait(false);
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new DoAsyncIteratorWithTask<TSource>(source, onNext, onError, onCompleted);
@@ -270,7 +282,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (true)
                     {
@@ -305,6 +319,10 @@ namespace System.Linq
                         await onCompleted(cancellationToken).ConfigureAwait(false);
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new DoAsyncIteratorWithTaskAndCancellation<TSource>(source, onNext, onError, onCompleted);

+ 7 - 1
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/IsEmpty.cs

@@ -19,10 +19,16 @@ namespace System.Linq
 
             static async Task<bool> Core(IAsyncEnumerable<TSource> _source, CancellationToken _cancellationToken)
             {
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     return !await e.MoveNextAsync();
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
         }
     }

+ 4 - 26
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Max.cs

@@ -24,8 +24,9 @@ namespace System.Linq
                     _comparer = Comparer<TSource>.Default;
                 }
 
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                         throw Error.NoElements();
@@ -42,35 +43,12 @@ namespace System.Linq
                         }
                     }
 
-                    return max;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        throw Error.NoElements();
-
-                    var max = e.Current;
-
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var cur = e.Current;
-
-                        if (_comparer.Compare(cur, max) > 0)
-                        {
-                            max = cur;
-                        }
-                    }
-
                     return max;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
             }
         }
     }

+ 4 - 26
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Min.cs

@@ -24,8 +24,9 @@ namespace System.Linq
                     _comparer = Comparer<TSource>.Default;
                 }
 
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                         throw Error.NoElements();
@@ -42,35 +43,12 @@ namespace System.Linq
                         }
                     }
 
-                    return min;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        throw Error.NoElements();
-
-                    var min = e.Current;
-
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var cur = e.Current;
-
-                        if (_comparer.Compare(cur, min) < 0)
-                        {
-                            min = cur;
-                        }
-                    }
-
                     return min;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
             }
         }
     }

+ 12 - 102
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/MinBy.cs

@@ -110,8 +110,9 @@ namespace System.Linq
         {
             var result = new List<TSource>();
 
-#if USE_AWAIT_USING
-            await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+            var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+            try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
             {
                 if (!await e.MoveNextAsync())
                     throw Error.NoElements();
@@ -127,36 +128,6 @@ namespace System.Linq
 
                     var cmp = compare(key, resKey);
 
-                    if (cmp == 0)
-                    {
-                        result.Add(cur);
-                    }
-                    else if (cmp > 0)
-                    {
-                        result = new List<TSource> { cur };
-                        resKey = key;
-                    }
-                }
-            }
-#else
-            var e = source.GetAsyncEnumerator(cancellationToken);
-
-            try
-            {
-                if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    throw Error.NoElements();
-
-                var current = e.Current;
-                var resKey = keySelector(current);
-                result.Add(current);
-
-                while (await e.MoveNextAsync().ConfigureAwait(false))
-                {
-                    var cur = e.Current;
-                    var key = keySelector(cur);
-
-                    var cmp = compare(key, resKey);
-
                     if (cmp == 0)
                     {
                         result.Add(cur);
@@ -170,9 +141,8 @@ namespace System.Linq
             }
             finally
             {
-                await e.DisposeAsync().ConfigureAwait(false);
+                await e.DisposeAsync();
             }
-#endif
 
             return result;
         }
@@ -181,8 +151,9 @@ namespace System.Linq
         {
             var result = new List<TSource>();
 
-#if USE_AWAIT_USING
-            await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+            var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+            try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
             {
                 if (!await e.MoveNextAsync())
                     throw Error.NoElements();
@@ -198,36 +169,6 @@ namespace System.Linq
 
                     var cmp = compare(key, resKey);
 
-                    if (cmp == 0)
-                    {
-                        result.Add(cur);
-                    }
-                    else if (cmp > 0)
-                    {
-                        result = new List<TSource> { cur };
-                        resKey = key;
-                    }
-                }
-            }
-#else
-            var e = source.GetAsyncEnumerator(cancellationToken);
-
-            try
-            {
-                if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    throw Error.NoElements();
-
-                var current = e.Current;
-                var resKey = await keySelector(current).ConfigureAwait(false);
-                result.Add(current);
-
-                while (await e.MoveNextAsync().ConfigureAwait(false))
-                {
-                    var cur = e.Current;
-                    var key = await keySelector(cur).ConfigureAwait(false);
-
-                    var cmp = compare(key, resKey);
-
                     if (cmp == 0)
                     {
                         result.Add(cur);
@@ -241,9 +182,8 @@ namespace System.Linq
             }
             finally
             {
-                await e.DisposeAsync().ConfigureAwait(false);
+                await e.DisposeAsync();
             }
-#endif
 
             return result;
         }
@@ -253,8 +193,9 @@ namespace System.Linq
         {
             var result = new List<TSource>();
 
-#if USE_AWAIT_USING
-            await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+            var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+            try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
             {
                 if (!await e.MoveNextAsync())
                     throw Error.NoElements();
@@ -270,36 +211,6 @@ namespace System.Linq
 
                     var cmp = compare(key, resKey);
 
-                    if (cmp == 0)
-                    {
-                        result.Add(cur);
-                    }
-                    else if (cmp > 0)
-                    {
-                        result = new List<TSource> { cur };
-                        resKey = key;
-                    }
-                }
-            }
-#else
-            var e = source.GetAsyncEnumerator(cancellationToken);
-
-            try
-            {
-                if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    throw Error.NoElements();
-
-                var current = e.Current;
-                var resKey = await keySelector(current, cancellationToken).ConfigureAwait(false);
-                result.Add(current);
-
-                while (await e.MoveNextAsync().ConfigureAwait(false))
-                {
-                    var cur = e.Current;
-                    var key = await keySelector(cur, cancellationToken).ConfigureAwait(false);
-
-                    var cmp = compare(key, resKey);
-
                     if (cmp == 0)
                     {
                         result.Add(cur);
@@ -313,9 +224,8 @@ namespace System.Linq
             }
             finally
             {
-                await e.DisposeAsync().ConfigureAwait(false);
+                await e.DisposeAsync();
             }
-#endif
 
             return result;
         }

+ 21 - 3
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Scan.cs

@@ -27,7 +27,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -43,6 +45,10 @@ namespace System.Linq
                         yield return res;
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new ScanAsyncEnumerable<TSource>(source, accumulator);
@@ -87,7 +93,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -103,6 +111,10 @@ namespace System.Linq
                         yield return res;
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new ScanAsyncEnumerableWithTask<TSource>(source, accumulator);
@@ -122,7 +134,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -138,6 +152,10 @@ namespace System.Linq
                         yield return res;
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new ScanAsyncEnumerableWithTaskAndCancellation<TSource>(source, accumulator);

+ 4 - 54
Ix.NET/Source/System.Linq.Async/System/Linq/AsyncEnumerableHelpers.cs

@@ -44,8 +44,9 @@ namespace System.Collections.Generic
             }
             else
             {
-#if USE_AWAIT_USING
-                await using (var en = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var en = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (await en.MoveNextAsync())
                     {
@@ -87,56 +88,6 @@ namespace System.Collections.Generic
                             arr[count++] = en.Current;
                         }
 
-                        result.Length = count;
-                        result.Array = arr;
-                        return result;
-                    }
-                }
-#else
-                var en = source.GetAsyncEnumerator(cancellationToken);
-
-                try
-                {
-                    if (await en.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        const int DefaultCapacity = 4;
-                        var arr = new T[DefaultCapacity];
-                        arr[0] = en.Current;
-                        var count = 1;
-
-                        while (await en.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            if (count == arr.Length)
-                            {
-                                // MaxArrayLength is defined in Array.MaxArrayLength and in gchelpers in CoreCLR.
-                                // It represents the maximum number of elements that can be in an array where
-                                // the size of the element is greater than one byte; a separate, slightly larger constant,
-                                // is used when the size of the element is one.
-                                const int MaxArrayLength = 0x7FEFFFFF;
-
-                                // This is the same growth logic as in List<T>:
-                                // If the array is currently empty, we make it a default size.  Otherwise, we attempt to 
-                                // double the size of the array.  Doubling will overflow once the size of the array reaches
-                                // 2^30, since doubling to 2^31 is 1 larger than Int32.MaxValue.  In that case, we instead 
-                                // constrain the length to be MaxArrayLength (this overflow check works because of the 
-                                // cast to uint).  Because a slightly larger constant is used when T is one byte in size, we 
-                                // could then end up in a situation where arr.Length is MaxArrayLength or slightly larger, such 
-                                // that we constrain newLength to be MaxArrayLength but the needed number of elements is actually 
-                                // larger than that.  For that case, we then ensure that the newLength is large enough to hold 
-                                // the desired capacity.  This does mean that in the very rare case where we've grown to such a 
-                                // large size, each new element added after MaxArrayLength will end up doing a resize.
-                                var newLength = count << 1;
-                                if ((uint)newLength > MaxArrayLength)
-                                {
-                                    newLength = MaxArrayLength <= count ? count + 1 : MaxArrayLength;
-                                }
-
-                                Array.Resize(ref arr, newLength);
-                            }
-
-                            arr[count++] = en.Current;
-                        }
-
                         result.Length = count;
                         result.Array = arr;
                         return result;
@@ -144,9 +95,8 @@ namespace System.Collections.Generic
                 }
                 finally
                 {
-                    await en.DisposeAsync().ConfigureAwait(false);
+                    await en.DisposeAsync();
                 }
-#endif
             }
 
             result.Length = 0;

+ 12 - 69
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Aggregate.cs

@@ -21,8 +21,9 @@ namespace System.Linq
 
             static async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, TSource, TSource> _accumulator, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -36,32 +37,12 @@ namespace System.Linq
                         acc = _accumulator(acc, e.Current);
                     }
 
-                    return acc;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    var acc = e.Current;
-
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        acc = _accumulator(acc, e.Current);
-                    }
-
                     return acc;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
             }
         }
 
@@ -76,8 +57,9 @@ namespace System.Linq
 
             static async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, TSource, ValueTask<TSource>> _accumulator, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -91,32 +73,12 @@ namespace System.Linq
                         acc = await _accumulator(acc, e.Current).ConfigureAwait(false);
                     }
 
-                    return acc;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    var acc = e.Current;
-
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        acc = await _accumulator(acc, e.Current).ConfigureAwait(false);
-                    }
-
                     return acc;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
             }
         }
 
@@ -132,8 +94,9 @@ namespace System.Linq
 
             static async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, TSource, CancellationToken, ValueTask<TSource>> _accumulator, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -147,32 +110,12 @@ namespace System.Linq
                         acc = await _accumulator(acc, e.Current, _cancellationToken).ConfigureAwait(false);
                     }
 
-                    return acc;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    var acc = e.Current;
-
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        acc = await _accumulator(acc, e.Current, _cancellationToken).ConfigureAwait(false);
-                    }
-
                     return acc;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
             }
         }
 #endif

+ 4 - 11
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Any.cs

@@ -19,23 +19,16 @@ namespace System.Linq
 
             static async Task<bool> Core(IAsyncEnumerable<TSource> _source, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
-                {
-                    return await e.MoveNextAsync();
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
 
-                try
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
-                    return await e.MoveNextAsync().ConfigureAwait(false);
+                    return await e.MoveNextAsync();
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
             }
         }
 

File diff suppressed because it is too large
+ 84 - 544
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.Generated.cs


+ 32 - 240
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Average.Generated.tt

@@ -58,8 +58,9 @@ foreach (var o in os)
 if (isNullable)
 {
 #>
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -81,44 +82,14 @@ if (isNullable)
                                 }
                             }
 
-                            return <#=res#>;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var v = e.Current;
-                        if (v.HasValue)
-                        {
-                            <#=o.sum#> sum = v.GetValueOrDefault();
-                            long count = 1;
-                            checked
-                            {
-                                while (await e.MoveNextAsync().ConfigureAwait(false))
-                                {
-                                    v = e.Current;
-                                    if (v.HasValue)
-                                    {
-                                        sum += v.GetValueOrDefault();
-                                        ++count;
-                                    }
-                                }
-                            }
-
                             return <#=res#>;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return null;
 <#
@@ -126,8 +97,9 @@ if (isNullable)
 else
 {
 #>
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -145,36 +117,12 @@ else
                         }
                     }
 
-                    return <#=res#>;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    <#=o.sum#> sum = e.Current;
-                    long count = 1;
-                    checked
-                    {
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            sum += e.Current;
-                            ++count;
-                        }
-                    }
-
                     return <#=res#>;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 <#
 }
 #>
@@ -196,8 +144,9 @@ else
 if (isNullable)
 {
 #>
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -219,44 +168,14 @@ if (isNullable)
                                 }
                             }
 
-                            return <#=res#>;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var v = _selector(e.Current);
-                        if (v.HasValue)
-                        {
-                            <#=o.sum#> sum = v.GetValueOrDefault();
-                            long count = 1;
-                            checked
-                            {
-                                while (await e.MoveNextAsync().ConfigureAwait(false))
-                                {
-                                    v = _selector(e.Current);
-                                    if (v.HasValue)
-                                    {
-                                        sum += v.GetValueOrDefault();
-                                        ++count;
-                                    }
-                                }
-                            }
-
                             return <#=res#>;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return null;
 <#
@@ -264,8 +183,9 @@ if (isNullable)
 else
 {
 #>
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -283,36 +203,12 @@ else
                         }
                     }
 
-                    return <#=res#>;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    <#=o.sum#> sum = _selector(e.Current);
-                    long count = 1;
-                    checked
-                    {
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            sum += _selector(e.Current);
-                            ++count;
-                        }
-                    }
-
                     return <#=res#>;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 <#
 }
 #>
@@ -334,8 +230,9 @@ else
 if (isNullable)
 {
 #>
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -357,44 +254,14 @@ if (isNullable)
                                 }
                             }
 
-                            return <#=res#>;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var v = await _selector(e.Current).ConfigureAwait(false);
-                        if (v.HasValue)
-                        {
-                            <#=o.sum#> sum = v.GetValueOrDefault();
-                            long count = 1;
-                            checked
-                            {
-                                while (await e.MoveNextAsync().ConfigureAwait(false))
-                                {
-                                    v = await _selector(e.Current).ConfigureAwait(false);
-                                    if (v.HasValue)
-                                    {
-                                        sum += v.GetValueOrDefault();
-                                        ++count;
-                                    }
-                                }
-                            }
-
                             return <#=res#>;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return null;
 <#
@@ -402,8 +269,9 @@ if (isNullable)
 else
 {
 #>
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -421,36 +289,12 @@ else
                         }
                     }
 
-                    return <#=res#>;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    <#=o.sum#> sum = await _selector(e.Current).ConfigureAwait(false);
-                    long count = 1;
-                    checked
-                    {
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            sum += await _selector(e.Current).ConfigureAwait(false);
-                            ++count;
-                        }
-                    }
-
                     return <#=res#>;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 <#
 }
 #>
@@ -473,8 +317,9 @@ else
 if (isNullable)
 {
 #>
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -496,44 +341,14 @@ if (isNullable)
                                 }
                             }
 
-                            return <#=res#>;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var v = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                        if (v.HasValue)
-                        {
-                            <#=o.sum#> sum = v.GetValueOrDefault();
-                            long count = 1;
-                            checked
-                            {
-                                while (await e.MoveNextAsync().ConfigureAwait(false))
-                                {
-                                    v = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                                    if (v.HasValue)
-                                    {
-                                        sum += v.GetValueOrDefault();
-                                        ++count;
-                                    }
-                                }
-                            }
-
                             return <#=res#>;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return null;
 <#
@@ -541,8 +356,9 @@ if (isNullable)
 else
 {
 #>
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -560,36 +376,12 @@ else
                         }
                     }
 
-                    return <#=res#>;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    <#=o.sum#> sum = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                    long count = 1;
-                    checked
-                    {
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            sum += await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                            ++count;
-                        }
-                    }
-
                     return <#=res#>;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 <#
 }
 #>

+ 16 - 71
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/FirstOrDefault.cs

@@ -97,29 +97,19 @@ namespace System.Linq
 
                 static async ValueTask<Maybe<TSource>> Core(IAsyncEnumerable<TSource> _source, CancellationToken _cancellationToken)
                 {
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
-                    {
-                        if (await e.MoveNextAsync())
-                        {
-                            return new Maybe<TSource>(e.Current);
-                        }
-                    }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
 
-                    try
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
-                        if (await e.MoveNextAsync().ConfigureAwait(false))
+                        if (await e.MoveNextAsync())
                         {
                             return new Maybe<TSource>(e.Current);
                         }
                     }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return new Maybe<TSource>();
                 }
@@ -130,25 +120,11 @@ namespace System.Linq
 
         private static async Task<Maybe<TSource>> TryGetFirst<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, bool> predicate, CancellationToken cancellationToken)
         {
-#if USE_AWAIT_USING
-            await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
-            {
-                while (await e.MoveNextAsync())
-                {
-                    var value = e.Current;
+            var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
 
-                    if (predicate(value))
-                    {
-                        return new Maybe<TSource>(value);
-                    }
-                }
-            }
-#else
-            var e = source.GetAsyncEnumerator(cancellationToken);
-
-            try
+            try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
             {
-                while (await e.MoveNextAsync().ConfigureAwait(false))
+                while (await e.MoveNextAsync())
                 {
                     var value = e.Current;
 
@@ -160,34 +136,19 @@ namespace System.Linq
             }
             finally
             {
-                await e.DisposeAsync().ConfigureAwait(false);
+                await e.DisposeAsync();
             }
-#endif
 
             return new Maybe<TSource>();
         }
 
         private static async Task<Maybe<TSource>> TryGetFirst<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, ValueTask<bool>> predicate, CancellationToken cancellationToken)
         {
-#if USE_AWAIT_USING
-            await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
-            {
-                while (await e.MoveNextAsync())
-                {
-                    var value = e.Current;
-
-                    if (await predicate(value).ConfigureAwait(false))
-                    {
-                        return new Maybe<TSource>(value);
-                    }
-                }
-            }
-#else
-            var e = source.GetAsyncEnumerator(cancellationToken);
+            var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
 
-            try
+            try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
             {
-                while (await e.MoveNextAsync().ConfigureAwait(false))
+                while (await e.MoveNextAsync())
                 {
                     var value = e.Current;
 
@@ -199,9 +160,8 @@ namespace System.Linq
             }
             finally
             {
-                await e.DisposeAsync().ConfigureAwait(false);
+                await e.DisposeAsync();
             }
-#endif
 
             return new Maybe<TSource>();
         }
@@ -209,25 +169,11 @@ namespace System.Linq
 #if !NO_DEEP_CANCELLATION
         private static async Task<Maybe<TSource>> TryGetFirst<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask<bool>> predicate, CancellationToken cancellationToken)
         {
-#if USE_AWAIT_USING
-            await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
-            {
-                while (await e.MoveNextAsync())
-                {
-                    var value = e.Current;
+            var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
 
-                    if (await predicate(value, cancellationToken).ConfigureAwait(false))
-                    {
-                        return new Maybe<TSource>(value);
-                    }
-                }
-            }
-#else
-            var e = source.GetAsyncEnumerator(cancellationToken);
-
-            try
+            try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
             {
-                while (await e.MoveNextAsync().ConfigureAwait(false))
+                while (await e.MoveNextAsync())
                 {
                     var value = e.Current;
 
@@ -239,9 +185,8 @@ namespace System.Linq
             }
             finally
             {
-                await e.DisposeAsync().ConfigureAwait(false);
+                await e.DisposeAsync();
             }
-#endif
 
             return new Maybe<TSource>();
         }

+ 21 - 3
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/GroupJoin.cs

@@ -31,7 +31,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (await e.MoveNextAsync())
                     {
@@ -46,6 +48,10 @@ namespace System.Linq
                         while (await e.MoveNextAsync());
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new GroupJoinAsyncEnumerable<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
@@ -73,7 +79,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (await e.MoveNextAsync())
                     {
@@ -88,6 +96,10 @@ namespace System.Linq
                         while (await e.MoveNextAsync());
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new GroupJoinAsyncEnumerableWithTask<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
@@ -116,7 +128,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (await e.MoveNextAsync())
                     {
@@ -131,6 +145,10 @@ namespace System.Linq
                         while (await e.MoveNextAsync());
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new GroupJoinAsyncEnumerableWithTaskAndCancellation<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);

+ 21 - 3
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Join.cs

@@ -32,7 +32,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (await e.MoveNextAsync())
                     {
@@ -63,6 +65,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new JoinAsyncIterator<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
@@ -90,7 +96,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (await e.MoveNextAsync())
                     {
@@ -121,6 +129,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new JoinAsyncIteratorWithTask<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
@@ -149,7 +161,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = outer.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (await e.MoveNextAsync())
                     {
@@ -180,6 +194,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new JoinAsyncIteratorWithTaskAndCancellation<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);

+ 32 - 212
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Max.cs

@@ -25,8 +25,9 @@ namespace System.Linq
 
                     var value = default(TSource);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         do
                         {
@@ -49,36 +50,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        do
-                        {
-                            if (!await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                return value;
-                            }
-
-                            value = e.Current;
-                        }
-                        while (value == null);
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = e.Current;
-                            if (x != null && comparer.Compare(x, value) > 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -93,8 +68,9 @@ namespace System.Linq
 
                     var value = default(TSource);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await e.MoveNextAsync())
                         {
@@ -112,31 +88,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            throw Error.NoElements();
-                        }
-
-                        value = e.Current;
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = e.Current;
-                            if (comparer.Compare(x, value) > 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -160,8 +115,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         do
                         {
@@ -184,36 +140,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        do
-                        {
-                            if (!await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                return value;
-                            }
-
-                            value = _selector(e.Current);
-                        }
-                        while (value == null);
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = _selector(e.Current);
-                            if (x != null && comparer.Compare(x, value) > 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -228,8 +158,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await e.MoveNextAsync())
                         {
@@ -247,31 +178,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            throw Error.NoElements();
-                        }
-
-                        value = _selector(e.Current);
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = _selector(e.Current);
-                            if (comparer.Compare(x, value) > 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -295,8 +205,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         do
                         {
@@ -319,36 +230,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        do
-                        {
-                            if (!await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                return value;
-                            }
-
-                            value = await _selector(e.Current).ConfigureAwait(false);
-                        }
-                        while (value == null);
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = await _selector(e.Current).ConfigureAwait(false);
-                            if (x != null && comparer.Compare(x, value) > 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -363,8 +248,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await e.MoveNextAsync())
                         {
@@ -383,31 +269,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            throw Error.NoElements();
-                        }
-
-                        value = await _selector(e.Current).ConfigureAwait(false);
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = await _selector(e.Current).ConfigureAwait(false);
-                            if (comparer.Compare(x, value) > 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -432,8 +297,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         do
                         {
@@ -456,36 +322,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        do
-                        {
-                            if (!await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                return value;
-                            }
-
-                            value = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                        }
-                        while (value == null);
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                            if (x != null && comparer.Compare(x, value) > 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -500,8 +340,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await e.MoveNextAsync())
                         {
@@ -520,31 +361,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            throw Error.NoElements();
-                        }
-
-                        value = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                            if (comparer.Compare(x, value) > 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }

+ 32 - 212
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Min.cs

@@ -25,8 +25,9 @@ namespace System.Linq
 
                     var value = default(TSource);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         do
                         {
@@ -49,36 +50,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        do
-                        {
-                            if (!await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                return value;
-                            }
-
-                            value = e.Current;
-                        }
-                        while (value == null);
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = e.Current;
-                            if (x != null && comparer.Compare(x, value) < 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -93,8 +68,9 @@ namespace System.Linq
 
                     var value = default(TSource);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await e.MoveNextAsync())
                         {
@@ -113,31 +89,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            throw Error.NoElements();
-                        }
-
-                        value = e.Current;
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = e.Current;
-                            if (comparer.Compare(x, value) < 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -161,8 +116,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         do
                         {
@@ -185,36 +141,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        do
-                        {
-                            if (!await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                return value;
-                            }
-
-                            value = _selector(e.Current);
-                        }
-                        while (value == null);
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = _selector(e.Current);
-                            if (x != null && comparer.Compare(x, value) < 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -229,8 +159,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await e.MoveNextAsync())
                         {
@@ -249,31 +180,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            throw Error.NoElements();
-                        }
-
-                        value = _selector(e.Current);
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = _selector(e.Current);
-                            if (comparer.Compare(x, value) < 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -297,8 +207,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         do
                         {
@@ -321,36 +232,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        do
-                        {
-                            if (!await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                return value;
-                            }
-
-                            value = await _selector(e.Current).ConfigureAwait(false);
-                        }
-                        while (value == null);
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = await _selector(e.Current).ConfigureAwait(false);
-                            if (x != null && comparer.Compare(x, value) < 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -365,8 +250,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await e.MoveNextAsync())
                         {
@@ -385,31 +271,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            throw Error.NoElements();
-                        }
-
-                        value = await _selector(e.Current).ConfigureAwait(false);
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = await _selector(e.Current).ConfigureAwait(false);
-                            if (comparer.Compare(x, value) < 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -434,8 +299,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         do
                         {
@@ -458,36 +324,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        do
-                        {
-                            if (!await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                return value;
-                            }
-
-                            value = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                        }
-                        while (value == null);
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                            if (x != null && comparer.Compare(x, value) < 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }
@@ -502,8 +342,9 @@ namespace System.Linq
 
                     var value = default(TResult);
 
-#if USE_AWAIT_USING
-                    await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         if (!await e.MoveNextAsync())
                         {
@@ -520,31 +361,10 @@ namespace System.Linq
                             }
                         }
                     }
-#else
-                    var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            throw Error.NoElements();
-                        }
-
-                        value = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var x = await _selector(e.Current, _cancellationToken).ConfigureAwait(false);
-                            if (comparer.Compare(x, value) < 0)
-                            {
-                                value = x;
-                            }
-                        }
-                    }
                     finally
                     {
-                        await e.DisposeAsync().ConfigureAwait(false);
+                        await e.DisposeAsync();
                     }
-#endif
 
                     return value;
                 }

File diff suppressed because it is too large
+ 141 - 592
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.cs


+ 16 - 462
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/MinMax.Generated.tt

@@ -43,8 +43,9 @@ foreach (var m in new[] { "Max", "Min" })
 #>
                 <#=t#> value;
 
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -104,80 +105,13 @@ foreach (var m in new[] { "Max", "Min" })
                         }
 <#
             }
-#>
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    value = e.Current;
-
-<#
-            if (isFloatingPoint && m == "Max")
-            {
-#>
-                    // NaN is ordered less than all other values. We need to do explicit checks
-                    // to ensure this, but once we've found a value that is not NaN we need no
-                    // longer worry about it, so first loop until such a value is found (or not,
-                    // as the case may be).
-
-                    while (<#=t#>.IsNaN(value))
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            return value;
-                        }
-
-                        value = e.Current;
-                    }
-
-<#
-            }
-#>
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var x = e.Current;
-                        if (x <#=comparison#> value)
-                        {
-                            value = x;
-                        }
-<#
-            if (isFloatingPoint && m == "Min")
-            {
-#>
-                        else
-                        {
-                            // Normally NaN < anything is false, as is anything < NaN
-                            // However, this leads to some irksome outcomes in Min and Max.
-                            // If we use those semantics then Min(NaN, 5.0) is NaN, but
-                            // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
-                            // ordering where NaN is smaller than every value, including
-                            // negative infinity.
-                            // Not testing for NaN therefore isn't an option, but since we
-                            // can't find a smaller value, we can short-circuit.
-
-                            if (<#=t#>.IsNaN(x))
-                            {
-                                return x;
-                            }
-                        }
-<#
-            }
 #>
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return value;
 <#
@@ -187,8 +121,9 @@ foreach (var m in new[] { "Max", "Min" })
 #>
                 <#=t#> value = null;
 
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     // Start off knowing that we've a non-null value (or exit here, knowing we don't)
                     // so we don't have to keep testing for nullity.
@@ -317,163 +252,6 @@ foreach (var m in new[] { "Max", "Min" })
                         var cur = e.Current;
                         var x = cur.GetValueOrDefault();
 
-<#
-                if (shortCircuit)
-                {
-#>
-                        if (cur.HasValue && x <#=comparison#> valueVal)
-<#
-                }
-                else
-                {
-#>
-                        // Do not replace & with &&. The branch prediction cost outweighs the extra operation
-                        // unless nulls either never happen or always happen.
-                        if (cur.HasValue & x <#=comparison#> valueVal)
-<#
-                }
-#>
-                        {
-                            valueVal = x;
-                            value = cur;
-                        }
-                    }
-<#
-            }
-#>
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    // Start off knowing that we've a non-null value (or exit here, knowing we don't)
-                    // so we don't have to keep testing for nullity.
-                    do
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            return value;
-                        }
-
-                        value = e.Current;
-                    }
-                    while (!value.HasValue);
-
-                    // Keep hold of the wrapped value, and do comparisons on that, rather than
-                    // using the lifted operation each time.
-                    var valueVal = value.GetValueOrDefault();
-
-<#
-            if (isInteger && m == "Max")
-            {
-#>
-                    if (valueVal >= 0)
-                    {
-                        // We can fast-path this case where we know HasValue will
-                        // never affect the outcome, without constantly checking
-                        // if we're in such a state. Similar fast-paths could
-                        // be done for other cases, but as all-positive or mostly-
-                        // positive integer values are quite common in real-world
-                        // uses, it's only been done for int? and long?.
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var cur = e.Current;
-                            var x = cur.GetValueOrDefault();
-
-                            if (x <#=comparison#> valueVal)
-                            {
-                                valueVal = x;
-                                value = cur;
-                            }
-                        }
-                    }
-                    else
-                    {
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var cur = e.Current;
-                            var x = cur.GetValueOrDefault();
-
-                            // Do not replace & with &&. The branch prediction cost outweighs the extra operation
-                            // unless nulls either never happen or always happen.
-                            if (cur.HasValue & x <#=comparison#> valueVal)
-                            {
-                                valueVal = x;
-                                value = cur;
-                            }
-                        }
-                    }
-<#
-            }
-            else if (isFloatingPoint && m == "Min")
-            {
-#>
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var cur = e.Current;
-                        if (cur.HasValue)
-                        {
-                            var x = cur.GetValueOrDefault();
-                            if (x <#=comparison#> valueVal)
-                            {
-                                valueVal = x;
-                                value = cur;
-                            }
-                            else
-                            {
-                                // Normally NaN < anything is false, as is anything < NaN
-                                // However, this leads to some irksome outcomes in Min and Max.
-                                // If we use those semantics then Min(NaN, 5.0) is NaN, but
-                                // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
-                                // ordering where NaN is smaller than every value, including
-                                // negative infinity.
-                                // Not testing for NaN therefore isn't an option, but since we
-                                // can't find a smaller value, we can short-circuit.
-
-                                if (<#=t.TrimEnd('?')#>.IsNaN(x))
-                                {
-                                    return cur;
-                                }
-                            }
-                        }
-                    }
-<#
-            }
-            else
-            {
-                if (isFloatingPoint && m == "Max")
-                {
-#>
-                    // NaN is ordered less than all other values. We need to do explicit checks
-                    // to ensure this, but once we've found a value that is not NaN we need no
-                    // longer worry about it, so first loop until such a value is found (or not,
-                    // as the case may be).
-
-                    while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            return value;
-                        }
-
-                        var cur = e.Current;
-
-                        if (cur.HasValue)
-                        {
-                            valueVal = (value = cur).GetValueOrDefault();
-                        }
-                    }
-
-<#
-                }
-#>
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var cur = e.Current;
-                        var x = cur.GetValueOrDefault();
-
 <#
                 if (shortCircuit)
                 {
@@ -501,9 +279,8 @@ foreach (var m in new[] { "Max", "Min" })
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return value;
 <#
@@ -545,8 +322,9 @@ foreach (var overload in new[] {
 #>
                 <#=t#> value;
 
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -606,80 +384,13 @@ foreach (var overload in new[] {
                         }
 <#
             }
-#>
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    value = <#=overload.invoke#>;
-
-<#
-            if (isFloatingPoint && m == "Max")
-            {
-#>
-                    // NaN is ordered less than all other values. We need to do explicit checks
-                    // to ensure this, but once we've found a value that is not NaN we need no
-                    // longer worry about it, so first loop until such a value is found (or not,
-                    // as the case may be).
-
-                    while (<#=t#>.IsNaN(value))
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            return value;
-                        }
-
-                        value = <#=overload.invoke#>;
-                    }
-
-<#
-            }
-#>
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var x = <#=overload.invoke#>;
-                        if (x <#=comparison#> value)
-                        {
-                            value = x;
-                        }
-<#
-            if (isFloatingPoint && m == "Min")
-            {
-#>
-                        else
-                        {
-                            // Normally NaN < anything is false, as is anything < NaN
-                            // However, this leads to some irksome outcomes in Min and Max.
-                            // If we use those semantics then Min(NaN, 5.0) is NaN, but
-                            // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
-                            // ordering where NaN is smaller than every value, including
-                            // negative infinity.
-                            // Not testing for NaN therefore isn't an option, but since we
-                            // can't find a smaller value, we can short-circuit.
-
-                            if (<#=t#>.IsNaN(x))
-                            {
-                                return x;
-                            }
-                        }
-<#
-            }
 #>
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return value;
 <#
@@ -689,8 +400,9 @@ foreach (var overload in new[] {
 #>
                 <#=t#> value = null;
 
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     // Start off knowing that we've a non-null value (or exit here, knowing we don't)
                     // so we don't have to keep testing for nullity.
@@ -819,163 +531,6 @@ foreach (var overload in new[] {
                         var cur = <#=overload.invoke#>;
                         var x = cur.GetValueOrDefault();
 
-<#
-                if (shortCircuit)
-                {
-#>
-                        if (cur.HasValue && x <#=comparison#> valueVal)
-<#
-                }
-                else
-                {
-#>
-                        // Do not replace & with &&. The branch prediction cost outweighs the extra operation
-                        // unless nulls either never happen or always happen.
-                        if (cur.HasValue & x <#=comparison#> valueVal)
-<#
-                }
-#>
-                        {
-                            valueVal = x;
-                            value = cur;
-                        }
-                    }
-<#
-            }
-#>
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    // Start off knowing that we've a non-null value (or exit here, knowing we don't)
-                    // so we don't have to keep testing for nullity.
-                    do
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            return value;
-                        }
-
-                        value = <#=overload.invoke#>;
-                    }
-                    while (!value.HasValue);
-
-                    // Keep hold of the wrapped value, and do comparisons on that, rather than
-                    // using the lifted operation each time.
-                    var valueVal = value.GetValueOrDefault();
-
-<#
-            if (isInteger && m == "Max")
-            {
-#>
-                    if (valueVal >= 0)
-                    {
-                        // We can fast-path this case where we know HasValue will
-                        // never affect the outcome, without constantly checking
-                        // if we're in such a state. Similar fast-paths could
-                        // be done for other cases, but as all-positive or mostly-
-                        // positive integer values are quite common in real-world
-                        // uses, it's only been done for int? and long?.
-
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var cur = <#=overload.invoke#>;
-                            var x = cur.GetValueOrDefault();
-
-                            if (x <#=comparison#> valueVal)
-                            {
-                                valueVal = x;
-                                value = cur;
-                            }
-                        }
-                    }
-                    else
-                    {
-                        while (await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            var cur = <#=overload.invoke#>;
-                            var x = cur.GetValueOrDefault();
-
-                            // Do not replace & with &&. The branch prediction cost outweighs the extra operation
-                            // unless nulls either never happen or always happen.
-                            if (cur.HasValue & x <#=comparison#> valueVal)
-                            {
-                                valueVal = x;
-                                value = cur;
-                            }
-                        }
-                    }
-<#
-            }
-            else if (isFloatingPoint && m == "Min")
-            {
-#>
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var cur = <#=overload.invoke#>;
-                        if (cur.HasValue)
-                        {
-                            var x = cur.GetValueOrDefault();
-                            if (x <#=comparison#> valueVal)
-                            {
-                                valueVal = x;
-                                value = cur;
-                            }
-                            else
-                            {
-                                // Normally NaN < anything is false, as is anything < NaN
-                                // However, this leads to some irksome outcomes in Min and Max.
-                                // If we use those semantics then Min(NaN, 5.0) is NaN, but
-                                // Min(5.0, NaN) is 5.0!  To fix this, we impose a total
-                                // ordering where NaN is smaller than every value, including
-                                // negative infinity.
-                                // Not testing for NaN therefore isn't an option, but since we
-                                // can't find a smaller value, we can short-circuit.
-
-                                if (<#=t.TrimEnd('?')#>.IsNaN(x))
-                                {
-                                    return cur;
-                                }
-                            }
-                        }
-                    }
-<#
-            }
-            else
-            {
-                if (isFloatingPoint && m == "Max")
-                {
-#>
-                    // NaN is ordered less than all other values. We need to do explicit checks
-                    // to ensure this, but once we've found a value that is not NaN we need no
-                    // longer worry about it, so first loop until such a value is found (or not,
-                    // as the case may be).
-
-                    while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
-                    {
-                        if (!await e.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            return value;
-                        }
-
-                        var cur = <#=overload.invoke#>;
-
-                        if (cur.HasValue)
-                        {
-                            valueVal = (value = cur).GetValueOrDefault();
-                        }
-                    }
-
-<#
-                }
-#>
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var cur = <#=overload.invoke#>;
-                        var x = cur.GetValueOrDefault();
-
 <#
                 if (shortCircuit)
                 {
@@ -1003,9 +558,8 @@ foreach (var overload in new[] {
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return value;
 <#

+ 10 - 66
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/OrderedAsyncEnumerable.cs

@@ -264,38 +264,11 @@ namespace System.Linq
 
         public async ValueTask<Maybe<TElement>> TryGetFirstAsync(CancellationToken cancellationToken)
         {
-#if USE_AWAIT_USING
-            await using (var e = _source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
-            {
-                if (!await e.MoveNextAsync())
-                {
-                    return new Maybe<TElement>();
-                }
-
-                var value = e.Current;
-
-                var comparer = GetComparer();
+            var e = _source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
 
-                await comparer.SetElement(value, cancellationToken).ConfigureAwait(false);
-
-                while (await e.MoveNextAsync())
-                {
-                    var x = e.Current;
-
-                    if (await comparer.Compare(x, cacheLower: true, cancellationToken).ConfigureAwait(false) < 0)
-                    {
-                        value = x;
-                    }
-                }
-
-                return new Maybe<TElement>(value);
-            }
-#else
-            IAsyncEnumerator<TElement> e = _source.GetAsyncEnumerator(cancellationToken);
-
-            try
+            try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
             {
-                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                if (!await e.MoveNextAsync())
                 {
                     return new Maybe<TElement>();
                 }
@@ -306,7 +279,7 @@ namespace System.Linq
 
                 await comparer.SetElement(value, cancellationToken).ConfigureAwait(false);
 
-                while (await e.MoveNextAsync().ConfigureAwait(false))
+                while (await e.MoveNextAsync())
                 {
                     TElement x = e.Current;
 
@@ -320,45 +293,17 @@ namespace System.Linq
             }
             finally
             {
-                await e.DisposeAsync().ConfigureAwait(false);
+                await e.DisposeAsync();
             }
-#endif
         }
 
         public async ValueTask<Maybe<TElement>> TryGetLastAsync(CancellationToken cancellationToken)
         {
-#if USE_AWAIT_USING
-            await using (var e = _source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
-            {
-                if (!await e.MoveNextAsync())
-                {
-                    return new Maybe<TElement>();
-                }
-
-                var value = e.Current;
-
-                var comparer = GetComparer();
+            var e = _source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
 
-                await comparer.SetElement(value, cancellationToken).ConfigureAwait(false);
-
-                while (await e.MoveNextAsync())
-                {
-                    var current = e.Current;
-
-                    if (await comparer.Compare(current, cacheLower: false, cancellationToken).ConfigureAwait(false) >= 0)
-                    {
-                        value = current;
-                    }
-                }
-
-                return new Maybe<TElement>(value);
-            }
-#else
-            IAsyncEnumerator<TElement> e = _source.GetAsyncEnumerator(cancellationToken);
-
-            try
+            try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
             {
-                if (!await e.MoveNextAsync().ConfigureAwait(false))
+                if (!await e.MoveNextAsync())
                 {
                     return new Maybe<TElement>();
                 }
@@ -369,7 +314,7 @@ namespace System.Linq
 
                 await comparer.SetElement(value, cancellationToken).ConfigureAwait(false);
 
-                while (await e.MoveNextAsync().ConfigureAwait(false))
+                while (await e.MoveNextAsync())
                 {
                     TElement current = e.Current;
 
@@ -383,9 +328,8 @@ namespace System.Linq
             }
             finally
             {
-                await e.DisposeAsync().ConfigureAwait(false);
+                await e.DisposeAsync();
             }
-#endif
         }
 
         internal async ValueTask<Maybe<TElement>> TryGetLastAsync(int minIndexInclusive, int maxIndexInclusive, CancellationToken cancellationToken)

+ 8 - 26
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SequenceEqual.cs

@@ -52,10 +52,13 @@ namespace System.Linq
 
             static async Task<bool> Core(IAsyncEnumerable<TSource> _first, IAsyncEnumerable<TSource> _second, IEqualityComparer<TSource> _comparer, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e1 = _first.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e1 = _first.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
-                    await using (var e2 = _second.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                    var e2 = _second.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         while (await e1.MoveNextAsync())
                         {
@@ -67,36 +70,15 @@ namespace System.Linq
 
                         return !await e2.MoveNextAsync();
                     }
-                }
-#else
-                var e1 = _first.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    var e2 = _second.GetAsyncEnumerator(_cancellationToken);
-
-                    try
-                    {
-                        while (await e1.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            if (!(await e2.MoveNextAsync().ConfigureAwait(false) && _comparer.Equals(e1.Current, e2.Current)))
-                            {
-                                return false;
-                            }
-                        }
-
-                        return !await e2.MoveNextAsync().ConfigureAwait(false);
-                    }
                     finally
                     {
-                        await e2.DisposeAsync().ConfigureAwait(false);
+                        await e2.DisposeAsync();
                     }
                 }
                 finally
                 {
-                    await e1.DisposeAsync().ConfigureAwait(false);
+                    await e1.DisposeAsync();
                 }
-#endif
             }
         }
     }

+ 16 - 103
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Single.cs

@@ -30,8 +30,9 @@ namespace System.Linq
                     throw Error.MoreThanOneElement();
                 }
 
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -45,31 +46,12 @@ namespace System.Linq
                         throw Error.MoreThanOneElement();
                     }
 
-                    return result;
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.NoElements();
-                    }
-
-                    var result = e.Current;
-                    if (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        throw Error.MoreThanOneElement();
-                    }
-
                     return result;
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
             }
         }
 
@@ -84,8 +66,9 @@ namespace System.Linq
 
             static async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, bool> _predicate, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -101,38 +84,14 @@ namespace System.Linq
                                 }
                             }
 
-                            return result;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var result = e.Current;
-
-                        if (_predicate(result))
-                        {
-                            while (await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                if (_predicate(e.Current))
-                                {
-                                    throw Error.MoreThanOneElement();
-                                }
-                            }
-
                             return result;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 throw Error.NoElements();
             }
@@ -149,8 +108,9 @@ namespace System.Linq
 
             static async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, ValueTask<bool>> _predicate, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -166,38 +126,14 @@ namespace System.Linq
                                 }
                             }
 
-                            return result;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var result = e.Current;
-
-                        if (await _predicate(result).ConfigureAwait(false))
-                        {
-                            while (await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                if (await _predicate(e.Current).ConfigureAwait(false))
-                                {
-                                    throw Error.MoreThanOneElement();
-                                }
-                            }
-
                             return result;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 throw Error.NoElements();
             }
@@ -215,8 +151,9 @@ namespace System.Linq
 
             static async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, CancellationToken, ValueTask<bool>> _predicate, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -232,38 +169,14 @@ namespace System.Linq
                                 }
                             }
 
-                            return result;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var result = e.Current;
-
-                        if (await _predicate(result, _cancellationToken).ConfigureAwait(false))
-                        {
-                            while (await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                if (await _predicate(e.Current, _cancellationToken).ConfigureAwait(false))
-                                {
-                                    throw Error.MoreThanOneElement();
-                                }
-                            }
-
                             return result;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 throw Error.NoElements();
             }

+ 16 - 102
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SingleOrDefault.cs

@@ -30,8 +30,9 @@ namespace System.Linq
                     throw Error.MoreThanOneElement();
                 }
 
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -45,28 +46,10 @@ namespace System.Linq
                         return result;
                     }
                 }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        return default;
-                    }
-
-                    var result = e.Current;
-
-                    if (!await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        return result;
-                    }
-                }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 throw Error.MoreThanOneElement();
             }
@@ -83,8 +66,9 @@ namespace System.Linq
 
             async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, bool> _predicate, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -100,38 +84,14 @@ namespace System.Linq
                                 }
                             }
 
-                            return result;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var result = e.Current;
-
-                        if (_predicate(result))
-                        {
-                            while (await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                if (_predicate(e.Current))
-                                {
-                                    throw Error.MoreThanOneElement();
-                                }
-                            }
-
                             return result;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return default;
             }
@@ -148,8 +108,9 @@ namespace System.Linq
 
             async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, ValueTask<bool>> _predicate, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -165,38 +126,14 @@ namespace System.Linq
                                 }
                             }
 
-                            return result;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var result = e.Current;
-
-                        if (await _predicate(result).ConfigureAwait(false))
-                        {
-                            while (await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                if (await _predicate(e.Current).ConfigureAwait(false))
-                                {
-                                    throw Error.MoreThanOneElement();
-                                }
-                            }
-
                             return result;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return default;
             }
@@ -214,8 +151,9 @@ namespace System.Linq
 
             async Task<TSource> Core(IAsyncEnumerable<TSource> _source, Func<TSource, CancellationToken, ValueTask<bool>> _predicate, CancellationToken _cancellationToken)
             {
-#if USE_AWAIT_USING
-                await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
+                var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -231,38 +169,14 @@ namespace System.Linq
                                 }
                             }
 
-                            return result;
-                        }
-                    }
-                }
-#else
-                var e = _source.GetAsyncEnumerator(_cancellationToken);
-
-                try
-                {
-                    while (await e.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var result = e.Current;
-
-                        if (await _predicate(result, _cancellationToken).ConfigureAwait(false))
-                        {
-                            while (await e.MoveNextAsync().ConfigureAwait(false))
-                            {
-                                if (await _predicate(e.Current, _cancellationToken).ConfigureAwait(false))
-                                {
-                                    throw Error.MoreThanOneElement();
-                                }
-                            }
-
                             return result;
                         }
                     }
                 }
                 finally
                 {
-                    await e.DisposeAsync().ConfigureAwait(false);
+                    await e.DisposeAsync();
                 }
-#endif
 
                 return default;
             }

+ 7 - 1
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SkipLast.cs

@@ -35,7 +35,9 @@ namespace System.Linq
             {
                 var queue = new Queue<TSource>();
 
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -55,6 +57,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new SkipLastAsyncIterator<TSource>(source, count);

+ 42 - 6
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/SkipWhile.cs

@@ -23,7 +23,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -42,6 +44,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new SkipWhileAsyncIterator<TSource>(source, predicate);
@@ -60,7 +66,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     var index = -1;
 
@@ -86,6 +94,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new SkipWhileWithIndexAsyncIterator<TSource>(source, predicate);
@@ -104,7 +116,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -123,6 +137,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new SkipWhileAsyncIteratorWithTask<TSource>(source, predicate);
@@ -142,7 +160,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     while (await e.MoveNextAsync())
                     {
@@ -161,6 +181,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new SkipWhileAsyncIteratorWithTaskAndCancellation<TSource>(source, predicate);
@@ -180,7 +204,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     var index = -1;
 
@@ -206,6 +232,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new SkipWhileWithIndexAsyncIteratorWithTask<TSource>(source, predicate);
@@ -225,7 +255,9 @@ namespace System.Linq
 
             async IAsyncEnumerator<TSource> Core(CancellationToken cancellationToken)
             {
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     var index = -1;
 
@@ -251,6 +283,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
             }
 #else
             return new SkipWhileWithIndexAsyncIteratorWithTaskAndCancellation<TSource>(source, predicate);

+ 7 - 1
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/TakeLast.cs

@@ -28,7 +28,9 @@ namespace System.Linq
             {
                 Queue<TSource> queue;
 
-                await using (var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e = source.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
                     if (!await e.MoveNextAsync())
                     {
@@ -56,6 +58,10 @@ namespace System.Linq
                         }
                     }
                 }
+                finally
+                {
+                    await e.DisposeAsync();
+                }
 
                 Debug.Assert(queue.Count <= count);
                 do

+ 42 - 6
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Zip.cs

@@ -25,15 +25,27 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e1 = first.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e1 = first.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
-                    await using (var e2 = second.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                    var e2 = second.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         while (await e1.MoveNextAsync() && await e2.MoveNextAsync())
                         {
                             yield return selector(e1.Current, e2.Current);
                         }
                     }
+                    finally
+                    {
+                        await e2.DisposeAsync();
+                    }
+                }
+                finally
+                {
+                    await e1.DisposeAsync();
                 }
             }
 #else
@@ -55,15 +67,27 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e1 = first.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e1 = first.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
-                    await using (var e2 = second.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                    var e2 = second.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         while (await e1.MoveNextAsync() && await e2.MoveNextAsync())
                         {
                             yield return await selector(e1.Current, e2.Current).ConfigureAwait(false);
                         }
                     }
+                    finally
+                    {
+                        await e2.DisposeAsync();
+                    }
+                }
+                finally
+                {
+                    await e1.DisposeAsync();
                 }
             }
 #else
@@ -86,15 +110,27 @@ namespace System.Linq
 
             async IAsyncEnumerator<TResult> Core(CancellationToken cancellationToken)
             {
-                await using (var e1 = first.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                var e1 = first.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                 {
-                    await using (var e2 = second.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false))
+                    var e2 = second.GetAsyncEnumerator(cancellationToken).ConfigureAwait(false);
+
+                    try // REVIEW: Can use `await using` if we get pattern bind (HAS_AWAIT_USING_PATTERN_BIND)
                     {
                         while (await e1.MoveNextAsync() && await e2.MoveNextAsync())
                         {
                             yield return await selector(e1.Current, e2.Current, cancellationToken).ConfigureAwait(false);
                         }
                     }
+                    finally
+                    {
+                        await e2.DisposeAsync();
+                    }
+                }
+                finally
+                {
+                    await e1.DisposeAsync();
                 }
             }
 #else

+ 1 - 4
Ix.NET/Source/System.Linq.Async/System/Runtime/CompilerServices/ConfiguredCancelableAsyncEnumerable.cs

@@ -47,7 +47,7 @@ namespace System.Runtime.CompilerServices
 
         /// <summary>Provides an awaitable async enumerator that enables cancelable iteration and configured awaits.</summary>
         [StructLayout(LayoutKind.Auto)]
-        public readonly struct Enumerator : IAsyncDisposable // Workaround for https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-01-16.md
+        public readonly struct Enumerator
         {
             private readonly IAsyncEnumerator<T> _enumerator;
             private readonly bool _continueOnCapturedContext;
@@ -76,9 +76,6 @@ namespace System.Runtime.CompilerServices
             /// </summary>
             public ConfiguredValueTaskAwaitable DisposeAsync() =>
                 _enumerator.DisposeAsync().ConfigureAwait(_continueOnCapturedContext);
-
-            async ValueTask IAsyncDisposable.DisposeAsync() =>
-                await _enumerator.DisposeAsync().ConfigureAwait(_continueOnCapturedContext);
         }
     }
 }

+ 3 - 13
Ix.NET/Source/System.Linq.Async/System/Threading/Tasks/AsyncEnumerableExtensions.cs

@@ -32,26 +32,19 @@ namespace System.Threading.Tasks
 
 #endif
 
-        //
-        // REVIEW: `await using (var e = xs.GetAsyncEnumerator().ConfigureAwait(false)) { ... }` leads to the following error when using BCL types.
-        //
-        //         error CS8410: 'ConfiguredCancelableAsyncEnumerable<TSource>.Enumerator': type used in an async using statement must be implicitly convertible to 'System.IAsyncDisposable'
-        //
-        //         See https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-01-16.md#pattern-based-disposal-in-await-foreach for the issues with
-        //         `await foreach` (but not `await using`). This should be reviewed with the LDM. Also see https://github.com/dotnet/csharplang/issues/1623.
-        //
-#if BCL_HAS_CONFIGUREAWAIT && AWAIT_USING_REQUIRES_IASYNCDISPOSABLE
+#if BCL_HAS_CONFIGUREAWAIT
         public static ConfiguredAsyncEnumerator<T> ConfigureAwait<T>(this IAsyncEnumerator<T> enumerator, bool continueOnCapturedContext)
         {
             if (enumerator == null)
                 throw Error.ArgumentNull(nameof(enumerator));
 
+            // NB: We need our own copy of the struct to access the constructor.
             return new ConfiguredAsyncEnumerator<T>(enumerator, continueOnCapturedContext);
         }
 
         /// <summary>Provides an awaitable async enumerator that enables cancelable iteration and configured awaits.</summary>
         [StructLayout(LayoutKind.Auto)]
-        public readonly struct ConfiguredAsyncEnumerator<T> : IAsyncDisposable
+        public readonly struct ConfiguredAsyncEnumerator<T>
         {
             private readonly IAsyncEnumerator<T> _enumerator;
             private readonly bool _continueOnCapturedContext;
@@ -80,9 +73,6 @@ namespace System.Threading.Tasks
             /// </summary>
             public ConfiguredValueTaskAwaitable DisposeAsync() =>
                 _enumerator.DisposeAsync().ConfigureAwait(_continueOnCapturedContext);
-
-            async ValueTask IAsyncDisposable.DisposeAsync() =>
-                await _enumerator.DisposeAsync().ConfigureAwait(_continueOnCapturedContext);
         }
 #else
         public static ConfiguredCancelableAsyncEnumerable<T>.Enumerator ConfigureAwait<T>(this IAsyncEnumerator<T> enumerator, bool continueOnCapturedContext)

Some files were not shown because too many files changed in this diff