Bladeren bron

Moving non-standard Distinct overloads to AsyncEnumerableEx.

Bart De Smet 8 jaren geleden
bovenliggende
commit
ce06267ac5

+ 6 - 6
Ix.NET/Source/System.Interactive.Async.Tests/AsyncTests.Single.cs

@@ -1450,7 +1450,7 @@ namespace Tests
             AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct<int>(null));
             AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct<int>(null, new Eq()));
             AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct<int>(AsyncEnumerable.Return(42), null));
-            AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct<int, int>(AsyncEnumerable.Return(42), (Func<int, int>)null));
+            AssertThrows<ArgumentNullException>(() => AsyncEnumerableEx.Distinct<int, int>(AsyncEnumerable.Return(42), (Func<int, int>)null));
         }
 
         [Fact]
@@ -3259,12 +3259,12 @@ namespace Tests
         [Fact]
         public void DistinctKey_Null()
         {
-            AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct(default(IAsyncEnumerable<int>), x => x));
-            AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct(AsyncEnumerable.Return(42), default(Func<int, int>)));
+            AssertThrows<ArgumentNullException>(() => AsyncEnumerableEx.Distinct(default(IAsyncEnumerable<int>), x => x));
+            AssertThrows<ArgumentNullException>(() => AsyncEnumerableEx.Distinct(AsyncEnumerable.Return(42), default(Func<int, int>)));
 
-            AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct(default(IAsyncEnumerable<int>), x => x, EqualityComparer<int>.Default));
-            AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct(AsyncEnumerable.Return(42), default(Func<int, int>), EqualityComparer<int>.Default));
-            AssertThrows<ArgumentNullException>(() => AsyncEnumerable.Distinct(AsyncEnumerable.Return(42), x => x, null));
+            AssertThrows<ArgumentNullException>(() => AsyncEnumerableEx.Distinct(default(IAsyncEnumerable<int>), x => x, EqualityComparer<int>.Default));
+            AssertThrows<ArgumentNullException>(() => AsyncEnumerableEx.Distinct(AsyncEnumerable.Return(42), default(Func<int, int>), EqualityComparer<int>.Default));
+            AssertThrows<ArgumentNullException>(() => AsyncEnumerableEx.Distinct(AsyncEnumerable.Return(42), x => x, null));
         }
 
         [Fact]

+ 15 - 0
Ix.NET/Source/System.Interactive.Async/System/Diagnostics/CodeAnalysis/ExcludeFromCodeCoverageAttribute.cs

@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+#if NO_CODE_COVERAGE_ATTRIBUTE
+
+namespace System.Diagnostics.CodeAnalysis
+{
+    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false)]
+    internal sealed class ExcludeFromCodeCoverageAttribute : Attribute
+    {
+    }
+}
+
+#endif

+ 350 - 0
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Distinct.cs

@@ -0,0 +1,350 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Linq
+{
+    public static partial class AsyncEnumerableEx
+    {
+        public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
+        {
+            if (source == null)
+                throw new ArgumentNullException(nameof(source));
+            if (keySelector == null)
+                throw new ArgumentNullException(nameof(keySelector));
+
+            return source.Distinct(keySelector, EqualityComparer<TKey>.Default);
+        }
+
+        public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw new ArgumentNullException(nameof(source));
+            if (keySelector == null)
+                throw new ArgumentNullException(nameof(keySelector));
+            if (comparer == null)
+                throw new ArgumentNullException(nameof(comparer));
+
+            return new DistinctAsyncIterator<TSource, TKey>(source, keySelector, comparer);
+        }
+
+        public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector)
+        {
+            if (source == null)
+                throw new ArgumentNullException(nameof(source));
+            if (keySelector == null)
+                throw new ArgumentNullException(nameof(keySelector));
+
+            return source.Distinct(keySelector, EqualityComparer<TKey>.Default);
+        }
+
+        public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector, IEqualityComparer<TKey> comparer)
+        {
+            if (source == null)
+                throw new ArgumentNullException(nameof(source));
+            if (keySelector == null)
+                throw new ArgumentNullException(nameof(keySelector));
+            if (comparer == null)
+                throw new ArgumentNullException(nameof(comparer));
+
+            return new DistinctAsyncIteratorWithTask<TSource, TKey>(source, keySelector, comparer);
+        }
+
+        private sealed class DistinctAsyncIterator<TSource, TKey> : AsyncIterator<TSource>, IIListProvider<TSource>
+        {
+            private readonly IEqualityComparer<TKey> comparer;
+            private readonly Func<TSource, TKey> keySelector;
+            private readonly IAsyncEnumerable<TSource> source;
+
+            private IAsyncEnumerator<TSource> enumerator;
+            private Set<TKey> set;
+
+            public DistinctAsyncIterator(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(keySelector != null);
+                Debug.Assert(comparer != null);
+
+                this.source = source;
+                this.keySelector = keySelector;
+                this.comparer = comparer;
+            }
+
+            public async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
+            {
+                var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
+                return s.ToArray();
+            }
+
+            public async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
+            {
+                var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
+                return s;
+            }
+
+            public async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
+            {
+                if (onlyIfCheap)
+                {
+                    return -1;
+                }
+
+                var count = 0;
+                var s = new Set<TKey>(comparer);
+
+                var enu = source.GetAsyncEnumerator();
+
+                try
+                {
+                    while (await enu.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var item = enu.Current;
+                        if (s.Add(keySelector(item)))
+                        {
+                            count++;
+                        }
+                    }
+                }
+                finally
+                {
+                    await enu.DisposeAsync().ConfigureAwait(false);
+                }
+
+                return count;
+            }
+
+            public override AsyncIterator<TSource> Clone()
+            {
+                return new DistinctAsyncIterator<TSource, TKey>(source, keySelector, comparer);
+            }
+
+            public override async Task DisposeAsync()
+            {
+                if (enumerator != null)
+                {
+                    await enumerator.DisposeAsync().ConfigureAwait(false);
+                    enumerator = null;
+                    set = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async Task<bool> MoveNextCore()
+            {
+                switch (state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        enumerator = source.GetAsyncEnumerator();
+
+                        if (!await enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            await DisposeAsync().ConfigureAwait(false);
+                            return false;
+                        }
+
+                        var element = enumerator.Current;
+                        set = new Set<TKey>(comparer);
+                        set.Add(keySelector(element));
+                        current = element;
+
+                        state = AsyncIteratorState.Iterating;
+                        return true;
+
+                    case AsyncIteratorState.Iterating:
+                        while (await enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            element = enumerator.Current;
+                            if (set.Add(keySelector(element)))
+                            {
+                                current = element;
+                                return true;
+                            }
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+
+            private async Task<List<TSource>> FillSetAsync(CancellationToken cancellationToken)
+            {
+                var s = new Set<TKey>(comparer);
+                var r = new List<TSource>();
+
+                var enu = source.GetAsyncEnumerator();
+
+                try
+                {
+                    while (await enu.MoveNextAsync(cancellationToken).ConfigureAwait(false))
+                    {
+                        var item = enu.Current;
+                        if (s.Add(keySelector(item)))
+                        {
+                            r.Add(item);
+                        }
+                    }
+                }
+                finally
+                {
+                    await enu.DisposeAsync().ConfigureAwait(false);
+                }
+
+                return r;
+            }
+        }
+
+        private sealed class DistinctAsyncIteratorWithTask<TSource, TKey> : AsyncIterator<TSource>, IIListProvider<TSource>
+        {
+            private readonly IEqualityComparer<TKey> comparer;
+            private readonly Func<TSource, Task<TKey>> keySelector;
+            private readonly IAsyncEnumerable<TSource> source;
+
+            private IAsyncEnumerator<TSource> enumerator;
+            private Set<TKey> set;
+
+            public DistinctAsyncIteratorWithTask(IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector, IEqualityComparer<TKey> comparer)
+            {
+                Debug.Assert(source != null);
+                Debug.Assert(keySelector != null);
+                Debug.Assert(comparer != null);
+
+                this.source = source;
+                this.keySelector = keySelector;
+                this.comparer = comparer;
+            }
+
+            public async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
+            {
+                var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
+                return s.ToArray();
+            }
+
+            public async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
+            {
+                var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
+                return s;
+            }
+
+            public async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
+            {
+                if (onlyIfCheap)
+                {
+                    return -1;
+                }
+
+                var count = 0;
+                var s = new Set<TKey>(comparer);
+
+                var enu = source.GetAsyncEnumerator();
+
+                try
+                {
+                    while (await enu.MoveNextAsync().ConfigureAwait(false))
+                    {
+                        var item = enu.Current;
+                        if (s.Add(await keySelector(item).ConfigureAwait(false)))
+                        {
+                            count++;
+                        }
+                    }
+                }
+                finally
+                {
+                    await enu.DisposeAsync().ConfigureAwait(false);
+                }
+
+                return count;
+            }
+
+            public override AsyncIterator<TSource> Clone()
+            {
+                return new DistinctAsyncIteratorWithTask<TSource, TKey>(source, keySelector, comparer);
+            }
+
+            public override async Task DisposeAsync()
+            {
+                if (enumerator != null)
+                {
+                    await enumerator.DisposeAsync().ConfigureAwait(false);
+                    enumerator = null;
+                    set = null;
+                }
+
+                await base.DisposeAsync().ConfigureAwait(false);
+            }
+
+            protected override async Task<bool> MoveNextCore()
+            {
+                switch (state)
+                {
+                    case AsyncIteratorState.Allocated:
+                        enumerator = source.GetAsyncEnumerator();
+
+                        if (!await enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            await DisposeAsync().ConfigureAwait(false);
+                            return false;
+                        }
+
+                        var element = enumerator.Current;
+                        set = new Set<TKey>(comparer);
+                        set.Add(await keySelector(element).ConfigureAwait(false));
+                        current = element;
+
+                        state = AsyncIteratorState.Iterating;
+                        return true;
+
+                    case AsyncIteratorState.Iterating:
+                        while (await enumerator.MoveNextAsync().ConfigureAwait(false))
+                        {
+                            element = enumerator.Current;
+                            if (set.Add(await keySelector(element).ConfigureAwait(false)))
+                            {
+                                current = element;
+                                return true;
+                            }
+                        }
+
+                        break;
+                }
+
+                await DisposeAsync().ConfigureAwait(false);
+                return false;
+            }
+
+            private async Task<List<TSource>> FillSetAsync(CancellationToken cancellationToken)
+            {
+                var s = new Set<TKey>(comparer);
+                var r = new List<TSource>();
+
+                var enu = source.GetAsyncEnumerator();
+
+                try
+                {
+                    while (await enu.MoveNextAsync(cancellationToken).ConfigureAwait(false))
+                    {
+                        var item = enu.Current;
+                        if (s.Add(await keySelector(item).ConfigureAwait(false)))
+                        {
+                            r.Add(item);
+                        }
+                    }
+                }
+                finally
+                {
+                    await enu.DisposeAsync().ConfigureAwait(false);
+                }
+
+                return r;
+            }
+        }
+    }
+}

+ 152 - 0
Ix.NET/Source/System.Interactive.Async/System/Linq/Set.cs

@@ -0,0 +1,152 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information. 
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+// from https://github.com/dotnet/corefx/blob/ec2685715b01d12f16b08d0dfa326649b12db8ec/src/System.Linq/src/System/Linq/Set.cs
+namespace System.Linq
+{
+    [ExcludeFromCodeCoverage]
+    internal sealed class Set<TElement>
+    {
+        private readonly IEqualityComparer<TElement> _comparer;
+        private int[] _buckets;
+#if DEBUG
+        private bool _haveRemoved;
+#endif
+        private Slot[] _slots;
+
+        public Set(IEqualityComparer<TElement> comparer)
+        {
+            _comparer = comparer ?? EqualityComparer<TElement>.Default;
+            _buckets = new int[7];
+            _slots = new Slot[7];
+        }
+
+        internal int Count { get; private set; }
+
+        // If value is not in set, add it and return true; otherwise return false
+        public bool Add(TElement value)
+        {
+#if DEBUG
+            Debug.Assert(!_haveRemoved, "This class is optimised for never calling Add after Remove. If your changes need to do so, undo that optimization.");
+#endif
+            var hashCode = InternalGetHashCode(value);
+            for (var i = _buckets[hashCode%_buckets.Length] - 1; i >= 0; i = _slots[i]._next)
+            {
+                if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value))
+                {
+                    return false;
+                }
+            }
+
+            if (Count == _slots.Length)
+            {
+                Resize();
+            }
+
+            var index = Count;
+            Count++;
+            var bucket = hashCode%_buckets.Length;
+            _slots[index]._hashCode = hashCode;
+            _slots[index]._value = value;
+            _slots[index]._next = _buckets[bucket] - 1;
+            _buckets[bucket] = index + 1;
+            return true;
+        }
+
+        // If value is in set, remove it and return true; otherwise return false
+        public bool Remove(TElement value)
+        {
+#if DEBUG
+            _haveRemoved = true;
+#endif
+            var hashCode = InternalGetHashCode(value);
+            var bucket = hashCode%_buckets.Length;
+            var last = -1;
+            for (var i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i]._next)
+            {
+                if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value))
+                {
+                    if (last < 0)
+                    {
+                        _buckets[bucket] = _slots[i]._next + 1;
+                    }
+                    else
+                    {
+                        _slots[last]._next = _slots[i]._next;
+                    }
+
+                    _slots[i]._hashCode = -1;
+                    _slots[i]._value = default(TElement);
+                    _slots[i]._next = -1;
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        internal int InternalGetHashCode(TElement value)
+        {
+            // Handle comparer implementations that throw when passed null
+            return (value == null) ? 0 : _comparer.GetHashCode(value) & 0x7FFFFFFF;
+        }
+
+        internal TElement[] ToArray()
+        {
+#if DEBUG
+            Debug.Assert(!_haveRemoved, "Optimised ToArray cannot be called if Remove has been called.");
+#endif
+            var array = new TElement[Count];
+            for (var i = 0; i != array.Length; ++i)
+            {
+                array[i] = _slots[i]._value;
+            }
+
+            return array;
+        }
+
+        internal List<TElement> ToList()
+        {
+#if DEBUG
+            Debug.Assert(!_haveRemoved, "Optimised ToList cannot be called if Remove has been called.");
+#endif
+            var count = Count;
+            var list = new List<TElement>(count);
+            for (var i = 0; i != count; ++i)
+            {
+                list.Add(_slots[i]._value);
+            }
+
+            return list;
+        }
+
+        private void Resize()
+        {
+            var newSize = checked((Count*2) + 1);
+            var newBuckets = new int[newSize];
+            var newSlots = new Slot[newSize];
+            Array.Copy(_slots, 0, newSlots, 0, Count);
+            for (var i = 0; i < Count; i++)
+            {
+                var bucket = newSlots[i]._hashCode%newSize;
+                newSlots[i]._next = newBuckets[bucket] - 1;
+                newBuckets[bucket] = i + 1;
+            }
+
+            _buckets = newBuckets;
+            _slots = newSlots;
+        }
+
+        internal struct Slot
+        {
+            internal int _hashCode;
+            internal int _next;
+            internal TElement _value;
+        }
+    }
+}

+ 1 - 1
Ix.NET/Source/System.Linq.Async/System/Linq/IIListProvider.cs

@@ -11,7 +11,7 @@ namespace System.Linq
     /// <summary>
     /// An iterator that can produce an array or <see cref="List{TElement}"/> through an optimized path.
     /// </summary>
-    internal interface IIListProvider<TElement> : IAsyncEnumerable<TElement>
+    public interface IIListProvider<TElement> : IAsyncEnumerable<TElement>
     {
         /// <summary>
         /// Produce an array of the sequence through an optimized path.

+ 0 - 336
Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Distinct.cs

@@ -29,50 +29,6 @@ namespace System.Linq
             return new DistinctAsyncIterator<TSource>(source, comparer);
         }
 
-        public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector)
-        {
-            if (source == null)
-                throw new ArgumentNullException(nameof(source));
-            if (keySelector == null)
-                throw new ArgumentNullException(nameof(keySelector));
-
-            return source.Distinct(keySelector, EqualityComparer<TKey>.Default);
-        }
-
-        public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
-        {
-            if (source == null)
-                throw new ArgumentNullException(nameof(source));
-            if (keySelector == null)
-                throw new ArgumentNullException(nameof(keySelector));
-            if (comparer == null)
-                throw new ArgumentNullException(nameof(comparer));
-
-            return new DistinctAsyncIterator<TSource, TKey>(source, keySelector, comparer);
-        }
-
-        public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector)
-        {
-            if (source == null)
-                throw new ArgumentNullException(nameof(source));
-            if (keySelector == null)
-                throw new ArgumentNullException(nameof(keySelector));
-
-            return source.Distinct(keySelector, EqualityComparer<TKey>.Default);
-        }
-
-        public static IAsyncEnumerable<TSource> Distinct<TSource, TKey>(this IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector, IEqualityComparer<TKey> comparer)
-        {
-            if (source == null)
-                throw new ArgumentNullException(nameof(source));
-            if (keySelector == null)
-                throw new ArgumentNullException(nameof(keySelector));
-            if (comparer == null)
-                throw new ArgumentNullException(nameof(comparer));
-
-            return new DistinctAsyncIteratorWithTask<TSource, TKey>(source, keySelector, comparer);
-        }
-
         private sealed class DistinctAsyncIterator<TSource> : AsyncIterator<TSource>, IIListProvider<TSource>
         {
             private readonly IEqualityComparer<TSource> comparer;
@@ -182,297 +138,5 @@ namespace System.Linq
                 return s;
             }
         }
-
-        private sealed class DistinctAsyncIterator<TSource, TKey> : AsyncIterator<TSource>, IIListProvider<TSource>
-        {
-            private readonly IEqualityComparer<TKey> comparer;
-            private readonly Func<TSource, TKey> keySelector;
-            private readonly IAsyncEnumerable<TSource> source;
-
-            private IAsyncEnumerator<TSource> enumerator;
-            private Set<TKey> set;
-
-            public DistinctAsyncIterator(IAsyncEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
-            {
-                Debug.Assert(source != null);
-                Debug.Assert(keySelector != null);
-                Debug.Assert(comparer != null);
-
-                this.source = source;
-                this.keySelector = keySelector;
-                this.comparer = comparer;
-            }
-
-            public async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
-            {
-                var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
-                return s.ToArray();
-            }
-
-            public async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
-            {
-                var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
-                return s;
-            }
-
-            public async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
-            {
-                if (onlyIfCheap)
-                {
-                    return -1;
-                }
-
-                var count = 0;
-                var s = new Set<TKey>(comparer);
-
-                var enu = source.GetAsyncEnumerator();
-
-                try
-                {
-                    while (await enu.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var item = enu.Current;
-                        if (s.Add(keySelector(item)))
-                        {
-                            count++;
-                        }
-                    }
-                }
-                finally
-                {
-                    await enu.DisposeAsync().ConfigureAwait(false);
-                }
-
-                return count;
-            }
-
-            public override AsyncIterator<TSource> Clone()
-            {
-                return new DistinctAsyncIterator<TSource, TKey>(source, keySelector, comparer);
-            }
-
-            public override async Task DisposeAsync()
-            {
-                if (enumerator != null)
-                {
-                    await enumerator.DisposeAsync().ConfigureAwait(false);
-                    enumerator = null;
-                    set = null;
-                }
-
-                await base.DisposeAsync().ConfigureAwait(false);
-            }
-
-            protected override async Task<bool> MoveNextCore()
-            {
-                switch (state)
-                {
-                    case AsyncIteratorState.Allocated:
-                        enumerator = source.GetAsyncEnumerator();
-
-                        if (!await enumerator.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            await DisposeAsync().ConfigureAwait(false);
-                            return false;
-                        }
-
-                        var element = enumerator.Current;
-                        set = new Set<TKey>(comparer);
-                        set.Add(keySelector(element));
-                        current = element;
-
-                        state = AsyncIteratorState.Iterating;
-                        return true;
-
-                    case AsyncIteratorState.Iterating:
-                        while (await enumerator.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            element = enumerator.Current;
-                            if (set.Add(keySelector(element)))
-                            {
-                                current = element;
-                                return true;
-                            }
-                        }
-
-                        break;
-                }
-
-                await DisposeAsync().ConfigureAwait(false);
-                return false;
-            }
-
-            private async Task<List<TSource>> FillSetAsync(CancellationToken cancellationToken)
-            {
-                var s = new Set<TKey>(comparer);
-                var r = new List<TSource>();
-
-                var enu = source.GetAsyncEnumerator();
-
-                try
-                {
-                    while (await enu.MoveNextAsync(cancellationToken).ConfigureAwait(false))
-                    {
-                        var item = enu.Current;
-                        if (s.Add(keySelector(item)))
-                        {
-                            r.Add(item);
-                        }
-                    }
-                }
-                finally
-                {
-                    await enu.DisposeAsync().ConfigureAwait(false);
-                }
-
-                return r;
-            }
-        }
-
-        private sealed class DistinctAsyncIteratorWithTask<TSource, TKey> : AsyncIterator<TSource>, IIListProvider<TSource>
-        {
-            private readonly IEqualityComparer<TKey> comparer;
-            private readonly Func<TSource, Task<TKey>> keySelector;
-            private readonly IAsyncEnumerable<TSource> source;
-
-            private IAsyncEnumerator<TSource> enumerator;
-            private Set<TKey> set;
-
-            public DistinctAsyncIteratorWithTask(IAsyncEnumerable<TSource> source, Func<TSource, Task<TKey>> keySelector, IEqualityComparer<TKey> comparer)
-            {
-                Debug.Assert(source != null);
-                Debug.Assert(keySelector != null);
-                Debug.Assert(comparer != null);
-
-                this.source = source;
-                this.keySelector = keySelector;
-                this.comparer = comparer;
-            }
-
-            public async Task<TSource[]> ToArrayAsync(CancellationToken cancellationToken)
-            {
-                var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
-                return s.ToArray();
-            }
-
-            public async Task<List<TSource>> ToListAsync(CancellationToken cancellationToken)
-            {
-                var s = await FillSetAsync(cancellationToken).ConfigureAwait(false);
-                return s;
-            }
-
-            public async Task<int> GetCountAsync(bool onlyIfCheap, CancellationToken cancellationToken)
-            {
-                if (onlyIfCheap)
-                {
-                    return -1;
-                }
-
-                var count = 0;
-                var s = new Set<TKey>(comparer);
-
-                var enu = source.GetAsyncEnumerator();
-
-                try
-                {
-                    while (await enu.MoveNextAsync().ConfigureAwait(false))
-                    {
-                        var item = enu.Current;
-                        if (s.Add(await keySelector(item).ConfigureAwait(false)))
-                        {
-                            count++;
-                        }
-                    }
-                }
-                finally
-                {
-                    await enu.DisposeAsync().ConfigureAwait(false);
-                }
-
-                return count;
-            }
-
-            public override AsyncIterator<TSource> Clone()
-            {
-                return new DistinctAsyncIteratorWithTask<TSource, TKey>(source, keySelector, comparer);
-            }
-
-            public override async Task DisposeAsync()
-            {
-                if (enumerator != null)
-                {
-                    await enumerator.DisposeAsync().ConfigureAwait(false);
-                    enumerator = null;
-                    set = null;
-                }
-
-                await base.DisposeAsync().ConfigureAwait(false);
-            }
-
-            protected override async Task<bool> MoveNextCore()
-            {
-                switch (state)
-                {
-                    case AsyncIteratorState.Allocated:
-                        enumerator = source.GetAsyncEnumerator();
-
-                        if (!await enumerator.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            await DisposeAsync().ConfigureAwait(false);
-                            return false;
-                        }
-
-                        var element = enumerator.Current;
-                        set = new Set<TKey>(comparer);
-                        set.Add(await keySelector(element).ConfigureAwait(false));
-                        current = element;
-
-                        state = AsyncIteratorState.Iterating;
-                        return true;
-
-                    case AsyncIteratorState.Iterating:
-                        while (await enumerator.MoveNextAsync().ConfigureAwait(false))
-                        {
-                            element = enumerator.Current;
-                            if (set.Add(await keySelector(element).ConfigureAwait(false)))
-                            {
-                                current = element;
-                                return true;
-                            }
-                        }
-
-                        break;
-                }
-
-                await DisposeAsync().ConfigureAwait(false);
-                return false;
-            }
-
-            private async Task<List<TSource>> FillSetAsync(CancellationToken cancellationToken)
-            {
-                var s = new Set<TKey>(comparer);
-                var r = new List<TSource>();
-
-                var enu = source.GetAsyncEnumerator();
-
-                try
-                {
-                    while (await enu.MoveNextAsync(cancellationToken).ConfigureAwait(false))
-                    {
-                        var item = enu.Current;
-                        if (s.Add(await keySelector(item).ConfigureAwait(false)))
-                        {
-                            r.Add(item);
-                        }
-                    }
-                }
-                finally
-                {
-                    await enu.DisposeAsync().ConfigureAwait(false);
-                }
-
-                return r;
-            }
-        }
     }
 }