From 5a1ea0a6ee21f44c189dd04cb980d38c9204aa8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tau=20G=C3=A4rtli?= Date: Wed, 28 Aug 2024 15:47:32 +0200 Subject: [PATCH 1/3] Implement ToImmutable*Async extension methods (#1545) --- .../AsyncOverloadsGenerator.cs | 37 +-- .../Collections/Immutable/ToImmutableArray.cs | 84 ++++++ .../Immutable/ToImmutableDictionary.cs | 113 ++++++++ .../Immutable/ToImmutableHashSet.cs | 49 ++++ .../Collections/Immutable/ToImmutableList.cs | 47 +++ .../Immutable/ToImmutableSortedDictionary.cs | 107 +++++++ .../Immutable/ToImmutableSortedSet.cs | 55 ++++ .../System.Linq.Async.csproj | 1 + .../Immutable/Operators/ToImmutableArray.cs | 35 +++ .../Operators/ToImmutableDictionary.cs | 267 ++++++++++++++++++ .../Immutable/Operators/ToImmutableHashSet.cs | 47 +++ .../Immutable/Operators/ToImmutableList.cs | 34 +++ .../Operators/ToImmutableSortedDictionary.cs | 222 +++++++++++++++ .../Operators/ToImmutableSortedSet.cs | 47 +++ .../System/Linq/FunctionalHelpers.cs | 23 ++ .../System/Linq/Operators/ToHashSet.cs | 18 +- .../System/Linq/Operators/ToList.cs | 15 +- .../System/Linq/Operators/Utilities.cs | 15 + .../System.Linq.Async.Ref.csproj | 2 + 19 files changed, 1169 insertions(+), 49 deletions(-) create mode 100644 Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableArray.cs create mode 100644 Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableDictionary.cs create mode 100644 Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableHashSet.cs create mode 100644 Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableList.cs create mode 100644 Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableSortedDictionary.cs create mode 100644 Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableSortedSet.cs create mode 100644 Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableArray.cs create mode 100644 Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableDictionary.cs create mode 100644 Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableHashSet.cs create mode 100644 Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableList.cs create mode 100644 Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableSortedDictionary.cs create mode 100644 Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableSortedSet.cs create mode 100644 Ix.NET/Source/System.Linq.Async/System/Linq/FunctionalHelpers.cs diff --git a/Ix.NET/Source/System.Linq.Async.SourceGenerator/AsyncOverloadsGenerator.cs b/Ix.NET/Source/System.Linq.Async.SourceGenerator/AsyncOverloadsGenerator.cs index b0c27ecfee..e497565114 100644 --- a/Ix.NET/Source/System.Linq.Async.SourceGenerator/AsyncOverloadsGenerator.cs +++ b/Ix.NET/Source/System.Linq.Async.SourceGenerator/AsyncOverloadsGenerator.cs @@ -52,28 +52,21 @@ private static IEnumerable GetMethodsGroupedBySyntaxTree(Ge private static string GenerateOverloads(AsyncMethodGrouping grouping, GenerationOptions options) { - var usings = grouping.SyntaxTree.GetRoot() is CompilationUnitSyntax compilationUnit - ? compilationUnit.Usings.ToString() - : string.Empty; - - var overloads = new StringBuilder(); - overloads.AppendLine("#nullable enable"); - overloads.AppendLine(usings); - overloads.AppendLine("namespace System.Linq"); - overloads.AppendLine("{"); - overloads.AppendLine(" partial class AsyncEnumerable"); - overloads.AppendLine(" {"); - - foreach (var method in grouping.Methods) - overloads.AppendLine(GenerateOverload(method, options)); - - overloads.AppendLine(" }"); - overloads.AppendLine("}"); - - return overloads.ToString(); + var compilationRoot = grouping.SyntaxTree.GetCompilationUnitRoot(); + var namespaceDeclaration = compilationRoot.ChildNodes().OfType().Single(); + var classDeclaration = namespaceDeclaration.ChildNodes().OfType().Single(); + + return CompilationUnit() + .WithUsings(List(compilationRoot.Usings.Select(@using => @using.WithoutTrivia()))) + .AddMembers(NamespaceDeclaration(namespaceDeclaration.Name) + .AddMembers(ClassDeclaration(classDeclaration.Identifier) + .AddModifiers(Token(SyntaxKind.PartialKeyword)) + .WithMembers(List(grouping.Methods.Select(method => GenerateOverload(method, options)))))) + .NormalizeWhitespace() + .ToFullString(); } - private static string GenerateOverload(AsyncMethod method, GenerationOptions options) + private static MemberDeclarationSyntax GenerateOverload(AsyncMethod method, GenerationOptions options) => MethodDeclaration(method.Syntax.ReturnType, GetMethodName(method.Symbol, options)) .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) .WithTypeParameterList(method.Syntax.TypeParameterList) @@ -87,9 +80,7 @@ private static string GenerateOverload(AsyncMethod method, GenerationOptions opt method.Syntax.ParameterList.Parameters .Select(p => Argument(IdentifierName(p.Identifier)))))))) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)) - .WithLeadingTrivia(method.Syntax.GetLeadingTrivia().Where(t => t.GetStructure() is not DirectiveTriviaSyntax)) - .NormalizeWhitespace() - .ToFullString(); + .WithLeadingTrivia(method.Syntax.GetLeadingTrivia().Where(t => t.GetStructure() is not DirectiveTriviaSyntax)); private static INamedTypeSymbol GetAsyncOverloadAttributeSymbol(GeneratorExecutionContext context) => context.Compilation.GetTypeByMetadataName("System.Linq.GenerateAsyncOverloadAttribute") ?? throw new InvalidOperationException(); diff --git a/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableArray.cs b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableArray.cs new file mode 100644 index 0000000000..8c0c9606a5 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableArray.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Tests +{ + public class ToImmutableArray : AsyncEnumerableTests + { + [Fact] + public async Task ToImmutableArray_Null() + { + await Assert.ThrowsAsync(() => ImmutableArrayAsyncEnumerableExtensions.ToImmutableArrayAsync(default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableArrayAsyncEnumerableExtensions.ToImmutableArrayAsync(default, CancellationToken.None).AsTask()); + } + + [Fact] + public async Task ToImmutableArray_IAsyncIListProvider_Simple() + { + var xs = new[] { 42, 25, 39 }; + var res = xs.ToAsyncEnumerable().ToImmutableArrayAsync(); + Assert.True((await res).SequenceEqual(xs)); + } + + [Fact] + public async Task ToImmutableArray_IAsyncIListProvider_Empty1() + { + var xs = new int[0]; + var res = xs.ToAsyncEnumerable().ToImmutableArrayAsync(); + Assert.True((await res).SequenceEqual(xs)); + } + + [Fact] + public async Task ToImmutableArray_IAsyncIListProvider_Empty2() + { + var xs = new HashSet(); + var res = xs.ToAsyncEnumerable().ToImmutableArrayAsync(); + Assert.True((await res).SequenceEqual(xs)); + } + + [Fact] + public async Task ToImmutableArray_Empty() + { + var xs = AsyncEnumerable.Empty(); + var res = xs.ToImmutableArrayAsync(); + Assert.True((await res).Length == 0); + } + + [Fact] + public async Task ToImmutableArray_Throw() + { + var ex = new Exception("Bang!"); + var res = Throw(ex).ToImmutableArrayAsync(); + await AssertThrowsAsync(res, ex); + } + + [Fact] + public async Task ToImmutableArray_Query() + { + var xs = await AsyncEnumerable.Range(5, 50).Take(10).ToImmutableArrayAsync(); + var ex = new[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + + Assert.True(ex.SequenceEqual(xs)); + } + + [Fact] + public async Task ToImmutableArray_Set() + { + var res = new[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + var xs = new HashSet(res); + + var arr = await xs.ToAsyncEnumerable().ToImmutableArrayAsync(); + + Assert.True(res.SequenceEqual(arr)); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableDictionary.cs b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableDictionary.cs new file mode 100644 index 0000000000..2ca697a579 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableDictionary.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Tests +{ + public class ToImmutableDictionary : AsyncEnumerableTests + { + [Fact] + public async Task ToImmutableDictionary_Null() + { + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(default, x => 0).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default(Func)).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(default, x => 0, EqualityComparer.Default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, EqualityComparer.Default).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(default, x => 0, x => 0).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, x => 0).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, x => 0, default).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(default, x => 0, x => 0, EqualityComparer.Default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, x => 0, EqualityComparer.Default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, x => 0, default, EqualityComparer.Default).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(default, x => 0, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default(Func), CancellationToken.None).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(default, x => 0, EqualityComparer.Default, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, EqualityComparer.Default, CancellationToken.None).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(default, x => 0, x => 0, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, x => 0, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, x => 0, default, CancellationToken.None).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(default, x => 0, x => 0, EqualityComparer.Default, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, x => 0, EqualityComparer.Default, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, x => 0, default, EqualityComparer.Default, CancellationToken.None).AsTask()); + } + + [Fact] + public async Task ToImmutableDictionary1Async() + { + var xs = new[] { 1, 4 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableDictionaryAsync(x => x % 2); + Assert.True(res[0] == 4); + Assert.True(res[1] == 1); + } + + [Fact] + public async Task ToImmutableDictionary2Async() + { + var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable(); + await AssertThrowsAsync(xs.ToImmutableDictionaryAsync(x => x % 2).AsTask()); + } + + [Fact] + public async Task ToImmutableDictionary3Async() + { + var xs = new[] { 1, 4 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableDictionaryAsync(x => x % 2, x => x + 1); + Assert.True(res[0] == 5); + Assert.True(res[1] == 2); + } + + [Fact] + public async Task ToImmutableDictionary4Async() + { + var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable(); + await AssertThrowsAsync(xs.ToImmutableDictionaryAsync(x => x % 2, x => x + 1).AsTask()); + } + + [Fact] + public async Task ToImmutableDictionary5Async() + { + var xs = new[] { 1, 4 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableDictionaryAsync(x => x % 2, new Eq()); + Assert.True(res[0] == 4); + Assert.True(res[1] == 1); + } + + [Fact] + public async Task ToImmutableDictionary6Async() + { + var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable(); + await AssertThrowsAsync(xs.ToImmutableDictionaryAsync(x => x % 2, new Eq()).AsTask()); + } + + [Fact] + public async Task ToImmutableDictionary7Async() + { + var xs = new[] { 1, 4 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableDictionaryAsync(x => x % 2, x => x, new Eq()); + Assert.True(res[0] == 4); + Assert.True(res[1] == 1); + } + + private sealed class Eq : IEqualityComparer + { + public bool Equals(int x, int y) => EqualityComparer.Default.Equals(Math.Abs(x), Math.Abs(y)); + + public int GetHashCode(int obj) => EqualityComparer.Default.GetHashCode(Math.Abs(obj)); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableHashSet.cs b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableHashSet.cs new file mode 100644 index 0000000000..c56e256076 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableHashSet.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Tests +{ + public class ToImmutableHashSet : AsyncEnumerableTests + { + [Fact] + public async Task ToImmutableHashSet_Null() + { + await Assert.ThrowsAsync(() => ImmutableHashSetAsyncEnumerableExtensions.ToImmutableHashSetAsync(default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableHashSetAsyncEnumerableExtensions.ToImmutableHashSetAsync(default, CancellationToken.None).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableHashSetAsyncEnumerableExtensions.ToImmutableHashSetAsync(default, EqualityComparer.Default, CancellationToken.None).AsTask()); + } + + [Fact] + public async Task ToImmutableHashSet_Simple() + { + var xs = new[] { 1, 2, 1, 2, 3, 4, 1, 2, 3, 4 }; + var res = xs.ToAsyncEnumerable().ToImmutableHashSetAsync(); + Assert.True((await res).OrderBy(x => x).SequenceEqual(new[] { 1, 2, 3, 4 })); + } + + [Fact] + public async Task ToImmutableHashSet_Comparer() + { + var xs = new[] { 1, 12, 11, 2, 3, 14, 1, 12, 13, 4 }; + var res = xs.ToAsyncEnumerable().ToImmutableHashSetAsync(new Eq()); + Assert.True((await res).OrderBy(x => x).SequenceEqual(new[] { 1, 3, 12, 14 })); + } + + private class Eq : IEqualityComparer + { + public bool Equals(int x, int y) => x % 10 == y % 10; + + public int GetHashCode(int obj) => obj % 10; + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableList.cs b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableList.cs new file mode 100644 index 0000000000..d3e5e7bfdf --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableList.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Tests +{ + public class ToImmutableList : AsyncEnumerableTests + { + [Fact] + public async Task ToImmutableList_Null() + { + await Assert.ThrowsAsync(() => ImmutableListAsyncEnumerableExtensions.ToImmutableListAsync(default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableListAsyncEnumerableExtensions.ToImmutableListAsync(default, CancellationToken.None).AsTask()); + } + + [Fact] + public async Task ToImmutableList_Simple() + { + var xs = new[] { 42, 25, 39 }; + var res = xs.ToAsyncEnumerable().ToImmutableListAsync(); + Assert.True((await res).SequenceEqual(xs)); + } + + [Fact] + public async Task ToImmutableList_Empty() + { + var xs = AsyncEnumerable.Empty(); + var res = xs.ToImmutableListAsync(); + Assert.True((await res).Count == 0); + } + + [Fact] + public async Task ToImmutableList_Throw() + { + var ex = new Exception("Bang!"); + var res = Throw(ex).ToImmutableListAsync(); + await AssertThrowsAsync(res, ex); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableSortedDictionary.cs b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableSortedDictionary.cs new file mode 100644 index 0000000000..d9ae5938f4 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableSortedDictionary.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Tests +{ + public class ToImmutableSortedDictionary : AsyncEnumerableTests + { + [Fact] + public async Task ToImmutableSortedDictionary_Null() + { + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(default, x => 0, x => 0).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(Return42, default, x => 0).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(Return42, x => 0, default).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(default, x => 0, x => 0, Comparer.Default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(Return42, default, x => 0, Comparer.Default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(Return42, x => 0, default, Comparer.Default).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(default, x => 0, x => 0, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(Return42, default, x => 0, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(Return42, x => 0, default, CancellationToken.None).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(default, x => 0, x => 0, Comparer.Default, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(Return42, default, x => 0, Comparer.Default, CancellationToken.None).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedDictionaryAsyncEnumerableExtensions.ToImmutableSortedDictionaryAsync(Return42, x => 0, default, Comparer.Default, CancellationToken.None).AsTask()); + } + + [Fact] + public async Task ToImmutableSortedDictionary1Async() + { + var xs = new[] { 1, 4 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableSortedDictionaryAsync(x => x % 2, x => x); + Assert.True(res[0] == 4); + Assert.True(res[1] == 1); + } + + [Fact] + public async Task ToImmutableSortedDictionary2Async() + { + var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable(); + await AssertThrowsAsync(xs.ToImmutableSortedDictionaryAsync(x => x % 2, x => x).AsTask()); + } + + [Fact] + public async Task ToImmutableSortedDictionary3Async() + { + var xs = new[] { 1, 4 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableSortedDictionaryAsync(x => x % 2, x => x + 1); + Assert.True(res[0] == 5); + Assert.True(res[1] == 2); + } + + [Fact] + public async Task ToImmutableSortedDictionary4Async() + { + var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable(); + await AssertThrowsAsync(xs.ToImmutableSortedDictionaryAsync(x => x % 2, x => x + 1).AsTask()); + } + + [Fact] + public async Task ToImmutableSortedDictionary5Async() + { + var xs = new[] { 1, 4 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableSortedDictionaryAsync(x => x % 2, x => x, new Eq()); + Assert.True(res[0] == 4); + Assert.True(res[1] == 1); + } + + [Fact] + public async Task ToImmutableSortedDictionary6Async() + { + var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable(); + await AssertThrowsAsync(xs.ToImmutableSortedDictionaryAsync(x => x % 2, x => x, new Eq()).AsTask()); + } + + [Fact] + public async Task ToImmutableSortedDictionary7Async() + { + var xs = new[] { 1, 4 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableSortedDictionaryAsync(x => x % 2, x => x, new Eq()); + Assert.True(res[0] == 4); + Assert.True(res[1] == 1); + } + + [Fact] + public async Task ToImmutableSortedDictionary8Async() + { + var xs = new[] { 5, 8, 7, 1, 9 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableSortedDictionaryAsync(x => x, x => x); + Assert.Equal(new[] { 1, 5, 7, 8, 9 }, res.Keys); + } + + private sealed class Eq : IComparer + { + public int Compare(int x, int y) => Comparer.Default.Compare(Math.Abs(x), Math.Abs(y)); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableSortedSet.cs b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableSortedSet.cs new file mode 100644 index 0000000000..7915484590 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async.Tests/System/Collections/Immutable/ToImmutableSortedSet.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Tests +{ + public class ToImmutableSortedSet : AsyncEnumerableTests + { + [Fact] + public async Task ToImmutableSortedSet_Null() + { + await Assert.ThrowsAsync(() => ImmutableSortedSetAsyncEnumerableExtensions.ToImmutableSortedSetAsync(default).AsTask()); + await Assert.ThrowsAsync(() => ImmutableSortedSetAsyncEnumerableExtensions.ToImmutableSortedSetAsync(default, CancellationToken.None).AsTask()); + + await Assert.ThrowsAsync(() => ImmutableSortedSetAsyncEnumerableExtensions.ToImmutableSortedSetAsync(default, Comparer.Default, CancellationToken.None).AsTask()); + } + + [Fact] + public async Task ToImmutableSortedSet_Simple() + { + var xs = new[] { 1, 2, 1, 2, 3, 4, 1, 2, 3, 4 }; + var res = xs.ToAsyncEnumerable().ToImmutableSortedSetAsync(); + Assert.Equal(new[] { 1, 2, 3, 4 }, await res); + } + + [Fact] + public async Task ToImmutableSortedSet_Comparer() + { + var xs = new[] { 1, 12, 11, 2, 3, 14, 1, 12, 13, 4 }; + var res = xs.ToAsyncEnumerable().ToImmutableSortedSetAsync(new Eq()); + Assert.Equal(new[] { 1, 12, 3, 14 }, await res); + } + + [Fact] + public async Task ToImmutableSortedSet_Sorted() + { + var xs = new[] { 5, 8, 7, 1, 9 }.ToAsyncEnumerable(); + var res = await xs.ToImmutableSortedSetAsync(); + Assert.Equal(new[] { 1, 5, 7, 8, 9 }, res); + } + + private sealed class Eq : IComparer + { + public int Compare(int x, int y) => Comparer.Default.Compare(x % 10, y % 10); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj b/Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj index 1bfe13cbef..09d7dd28b6 100644 --- a/Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj +++ b/Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj @@ -27,6 +27,7 @@ + diff --git a/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableArray.cs b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableArray.cs new file mode 100644 index 0000000000..7869a67739 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableArray.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Collections.Immutable +{ + public static class ImmutableArrayAsyncEnumerableExtensions + { + /// + /// Creates an immutable array from an async-enumerable sequence. + /// + /// The type of the elements in the source sequence. + /// The source async-enumerable sequence to get an array of elements for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with an array containing all the elements of the source sequence. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableArrayAsync( + this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + + return Core(source, cancellationToken); + + static async ValueTask> Core(IAsyncEnumerable source, CancellationToken cancellationToken) + => ImmutableArray.Create(await source.ToArrayAsync(cancellationToken).ConfigureAwait(false)); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableDictionary.cs b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableDictionary.cs new file mode 100644 index 0000000000..923c04f52d --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableDictionary.cs @@ -0,0 +1,267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using static System.Linq.FunctionalHelpers; + +namespace System.Collections.Immutable +{ + public static partial class ImmutableDictionaryAsyncEnumerableExtensions + { + /// + /// Creates an immutable dictionary from an async-enumerable sequence according to a specified key selector function. + /// + /// The type of the elements in the source sequence. + /// The type of the dictionary key computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// A function to extract a key from each element. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// or is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableDictionaryAsync(this IAsyncEnumerable source, Func keySelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAsync(keySelector, elementSelector: Identity, keyComparer: null, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable dictionary from an async-enumerable sequence according to a specified key selector function, and an element selector function. + /// + /// The type of the elements in the source sequence. + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// A function to extract a key from each element. + /// A transform function to produce a result element value from each element. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// , , or is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAsync(keySelector, elementSelector, keyComparer: null, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable dictionary from an async-enumerable sequence according to a specified key selector function, and a key equality comparer. + /// + /// The type of the elements in the source sequence. + /// The type of the dictionary key computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// A function to extract a key from each element. + /// An equality comparer to compare keys. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// or is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableDictionaryAsync(this IAsyncEnumerable source, Func keySelector, IEqualityComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAsync(keySelector, elementSelector: Identity, keyComparer, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable dictionary from an async-enumerable sequence according to a specified key selector function, an element selector function, and a key equality comparer. + /// + /// The type of the elements in the source sequence. + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// A function to extract a key from each element. + /// A transform function to produce a result element value from each element. + /// An equality comparer to compare keys. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// , , or is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAsync(keySelector, elementSelector, keyComparer, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable dictionary from an async-enumerable sequence of key/value pairs. + /// + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableDictionaryAsync(this IAsyncEnumerable> source, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAsync(keySelector: Key, elementSelector: Value, keyComparer: null, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable dictionary from an async-enumerable sequence of key/value pairs according to a specified key equality comparer. + /// + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An equality comparer to compare keys. + /// An async-enumerable sequence to create a dictionary for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableDictionaryAsync(this IAsyncEnumerable> source, IEqualityComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAsync(keySelector: Key, elementSelector: Value, keyComparer, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable dictionary from an async-enumerable sequence of key/value pairs according to a specified key equality comparer and a value equality comparer. + /// + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An equality comparer to compare keys. + /// An equality comparer to compare values. + /// An async-enumerable sequence to create a dictionary for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableDictionaryAsync(this IAsyncEnumerable> source, IEqualityComparer? keyComparer, IEqualityComparer? valueComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAsync(keySelector: Key, elementSelector: Value, keyComparer, valueComparer, cancellationToken); + + /// + /// Creates an immutable dictionary from an async-enumerable sequence according to a specified key selector function, an element selector function, a key equality comparer and a value equality comparer. + /// + /// The type of the elements in the source sequence. + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// A function to extract a key from each element. + /// A transform function to produce a result element value from each element. + /// An equality comparer to compare keys. + /// An equality comparer to compare values. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// , , or is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableDictionaryAsync( + this IAsyncEnumerable source, + Func keySelector, Func elementSelector, + IEqualityComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + where TKey : notnull + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + if (elementSelector == null) + throw Error.ArgumentNull(nameof(elementSelector)); + + return Core(source, keySelector, elementSelector, keyComparer, valueComparer, cancellationToken); + + static async ValueTask> Core( + IAsyncEnumerable source, + Func keySelector, Func elementSelector, + IEqualityComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + { + var builder = ImmutableDictionary.CreateBuilder(keyComparer, valueComparer); + + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + builder.Add(keySelector(item), elementSelector(item)); + } + + return builder.ToImmutable(); + } + } + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitAsyncCore(this IAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAwaitAsyncCore(keySelector, elementSelector: IdentityAsync, keyComparer: null, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitAsyncCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAwaitAsyncCore(keySelector, elementSelector, keyComparer: null, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitAsyncCore(this IAsyncEnumerable source, Func> keySelector, IEqualityComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAwaitAsyncCore(keySelector, elementSelector: IdentityAsync, keyComparer, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitAsyncCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAwaitAsyncCore(keySelector, elementSelector, keyComparer, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitAsyncCore( + this IAsyncEnumerable source, + Func> keySelector, Func> elementSelector, + IEqualityComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + where TKey : notnull + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + if (elementSelector == null) + throw Error.ArgumentNull(nameof(elementSelector)); + + return Core(source, keySelector, elementSelector, keyComparer, valueComparer, cancellationToken); + + static async ValueTask> Core( + IAsyncEnumerable source, + Func> keySelector, Func> elementSelector, + IEqualityComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + { + var builder = ImmutableDictionary.CreateBuilder(keyComparer, valueComparer); + + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + builder.Add(await keySelector(item).ConfigureAwait(false), await elementSelector(item).ConfigureAwait(false)); + } + + return builder.ToImmutable(); + } + } + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitWithCancellationAsyncCore(this IAsyncEnumerable source, Func> keySelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAwaitWithCancellationAsyncCore(keySelector, elementSelector: IdentityAsync, keyComparer: null, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitWithCancellationAsyncCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAwaitWithCancellationAsyncCore(keySelector, elementSelector, keyComparer: null, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitWithCancellationAsyncCore(this IAsyncEnumerable source, Func> keySelector, IEqualityComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAwaitWithCancellationAsyncCore(keySelector, elementSelector: IdentityAsync, keyComparer, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitWithCancellationAsyncCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, IEqualityComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableDictionaryAwaitWithCancellationAsyncCore(keySelector, elementSelector, keyComparer, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableDictionaryAwaitWithCancellationAsyncCore( + this IAsyncEnumerable source, + Func> keySelector, Func> elementSelector, + IEqualityComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + where TKey : notnull + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + if (elementSelector == null) + throw Error.ArgumentNull(nameof(elementSelector)); + + return Core(source, keySelector, elementSelector, keyComparer, valueComparer, cancellationToken); + + static async ValueTask> Core( + IAsyncEnumerable source, + Func> keySelector, Func> elementSelector, + IEqualityComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + { + var builder = ImmutableDictionary.CreateBuilder(keyComparer, valueComparer); + + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + builder.Add(await keySelector(item, cancellationToken).ConfigureAwait(false), await elementSelector(item, cancellationToken).ConfigureAwait(false)); + } + + return builder.ToImmutable(); + } + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableHashSet.cs b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableHashSet.cs new file mode 100644 index 0000000000..6858d65ac4 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableHashSet.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Collections.Immutable +{ + public static class ImmutableHashSetAsyncEnumerableExtensions + { + /// + /// Creates an immutable hash set from an async-enumerable sequence. + /// + /// The type of the elements in the source sequence. + /// The source async-enumerable sequence to get a hash set of elements for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a hash set containing all the elements of the source sequence. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableHashSetAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + => ToImmutableHashSetAsync(source, equalityComparer: null, cancellationToken); + + /// + /// Creates an immutable hash set from an async-enumerable sequence according to a specified equality comparer. + /// + /// The type of the elements in the source sequence. + /// The source async-enumerable sequence to get a hash set of elements for. + /// An equality comparer to compare elements of the sequence. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a hash set containing all the elements of the source sequence. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableHashSetAsync(this IAsyncEnumerable source, IEqualityComparer? equalityComparer, CancellationToken cancellationToken = default) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + + return source.ToCollection( + ImmutableHashSet.CreateBuilder(equalityComparer), + static builder => builder.ToImmutable(), + cancellationToken); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableList.cs b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableList.cs new file mode 100644 index 0000000000..919090e1e1 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableList.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Collections.Immutable +{ + public static class ImmutableListAsyncEnumerableExtensions + { + /// + /// Creates an immutable list from an async-enumerable sequence. + /// + /// The type of the elements in the source sequence. + /// The source async-enumerable sequence to get a list of elements for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a list containing all the elements of the source sequence. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableListAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + + return source.ToCollection( + ImmutableList.CreateBuilder(), + static builder => builder.ToImmutable(), + cancellationToken); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableSortedDictionary.cs b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableSortedDictionary.cs new file mode 100644 index 0000000000..51c6e36407 --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableSortedDictionary.cs @@ -0,0 +1,222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using static System.Linq.FunctionalHelpers; + +namespace System.Collections.Immutable +{ + public static partial class ImmutableSortedDictionaryAsyncEnumerableExtensions + { + /// + /// Creates an immutable sorted dictionary from an async-enumerable sequence according to a specified key selector function, and an element selector function. + /// + /// The type of the elements in the source sequence. + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// A function to extract a key from each element. + /// A transform function to produce a result element value from each element. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// , , or is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableSortedDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAsync(keySelector, elementSelector, keyComparer: null, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable sorted dictionary from an async-enumerable sequence according to a specified key selector function, an element selector function, and a key comparer. + /// + /// The type of the elements in the source sequence. + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// A function to extract a key from each element. + /// A transform function to produce a result element value from each element. + /// A comparer to compare keys. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// , , or is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableSortedDictionaryAsync(this IAsyncEnumerable source, Func keySelector, Func elementSelector, IComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAsync(keySelector, elementSelector, keyComparer, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable sorted dictionary from an async-enumerable sequence of key/value pairs. + /// + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableSortedDictionaryAsync(this IAsyncEnumerable> source, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAsync(keySelector: Key, elementSelector: Value, keyComparer: null, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable sorted dictionary from an async-enumerable sequence of key/value pairs according to a specified key comparer. + /// + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// A comparer to compare keys. + /// An async-enumerable sequence to create a dictionary for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableSortedDictionaryAsync(this IAsyncEnumerable> source, IComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAsync(keySelector: Key, elementSelector: Value, keyComparer, valueComparer: null, cancellationToken); + + /// + /// Creates an immutable sorted dictionary from an async-enumerable sequence of key/value pairs according to a specified key comparer and a value equality comparer. + /// + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// A comparer to compare keys. + /// An equality comparer to compare values. + /// An async-enumerable sequence to create a dictionary for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableSortedDictionaryAsync(this IAsyncEnumerable> source, IComparer? keyComparer, IEqualityComparer? valueComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAsync(keySelector: Key, elementSelector: Value, keyComparer, valueComparer, cancellationToken); + + /// + /// Creates an sorted immutable dictionary from an async-enumerable sequence according to a specified key selector function, an element selector function, a key comparer and a value equality comparer. + /// + /// The type of the elements in the source sequence. + /// The type of the dictionary key computed for each element in the source sequence. + /// The type of the dictionary value computed for each element in the source sequence. + /// An async-enumerable sequence to create a dictionary for. + /// A function to extract a key from each element. + /// A transform function to produce a result element value from each element. + /// A comparer to compare keys. + /// An equality comparer to compare values. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a dictionary mapping unique key values onto the corresponding source sequence's element. + /// , , or is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableSortedDictionaryAsync( + this IAsyncEnumerable source, + Func keySelector, Func elementSelector, + IComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + where TKey : notnull + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + if (elementSelector == null) + throw Error.ArgumentNull(nameof(elementSelector)); + + return Core(source, keySelector, elementSelector, keyComparer, valueComparer, cancellationToken); + + static async ValueTask> Core( + IAsyncEnumerable source, + Func keySelector, Func elementSelector, + IComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + { + var builder = ImmutableSortedDictionary.CreateBuilder(keyComparer, valueComparer); + + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + builder.Add(keySelector(item), elementSelector(item)); + } + + return builder.ToImmutable(); + } + } + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableSortedDictionaryAwaitAsyncCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAwaitAsyncCore(keySelector, elementSelector, keyComparer: null, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableSortedDictionaryAwaitAsyncCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, IComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAwaitAsyncCore(keySelector, elementSelector, keyComparer, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableSortedDictionaryAwaitAsyncCore( + this IAsyncEnumerable source, + Func> keySelector, Func> elementSelector, + IComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + where TKey : notnull + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + if (elementSelector == null) + throw Error.ArgumentNull(nameof(elementSelector)); + + return Core(source, keySelector, elementSelector, keyComparer, valueComparer, cancellationToken); + + static async ValueTask> Core( + IAsyncEnumerable source, + Func> keySelector, Func> elementSelector, + IComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + { + var builder = ImmutableSortedDictionary.CreateBuilder(keyComparer, valueComparer); + + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + builder.Add(await keySelector(item).ConfigureAwait(false), await elementSelector(item).ConfigureAwait(false)); + } + + return builder.ToImmutable(); + } + } + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableSortedDictionaryAwaitWithCancellationAsyncCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAwaitWithCancellationAsyncCore(keySelector, elementSelector, keyComparer: null, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableSortedDictionaryAwaitWithCancellationAsyncCore(this IAsyncEnumerable source, Func> keySelector, Func> elementSelector, IComparer? keyComparer, CancellationToken cancellationToken = default) where TKey : notnull + => source.ToImmutableSortedDictionaryAwaitWithCancellationAsyncCore(keySelector, elementSelector, keyComparer, valueComparer: null, cancellationToken); + + [GenerateAsyncOverload] + private static ValueTask> ToImmutableSortedDictionaryAwaitWithCancellationAsyncCore( + this IAsyncEnumerable source, + Func> keySelector, Func> elementSelector, + IComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + where TKey : notnull + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (keySelector == null) + throw Error.ArgumentNull(nameof(keySelector)); + if (elementSelector == null) + throw Error.ArgumentNull(nameof(elementSelector)); + + return Core(source, keySelector, elementSelector, keyComparer, valueComparer, cancellationToken); + + static async ValueTask> Core( + IAsyncEnumerable source, + Func> keySelector, Func> elementSelector, + IComparer? keyComparer, IEqualityComparer? valueComparer, + CancellationToken cancellationToken = default) + { + var builder = ImmutableSortedDictionary.CreateBuilder(keyComparer, valueComparer); + + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + builder.Add(await keySelector(item, cancellationToken).ConfigureAwait(false), await elementSelector(item, cancellationToken).ConfigureAwait(false)); + } + + return builder.ToImmutable(); + } + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableSortedSet.cs b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableSortedSet.cs new file mode 100644 index 0000000000..61f2536cde --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async/System/Collections/Immutable/Operators/ToImmutableSortedSet.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Collections.Immutable +{ + public static class ImmutableSortedSetAsyncEnumerableExtensions + { + /// + /// Creates an immutable sorted set from an async-enumerable sequence. + /// + /// The type of the elements in the source sequence. + /// The source async-enumerable sequence to get a hash set of elements for. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a sorted set containing all the elements of the source sequence. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableSortedSetAsync(this IAsyncEnumerable source, CancellationToken cancellationToken = default) + => ToImmutableSortedSetAsync(source, comparer: null, cancellationToken); + + /// + /// Creates an immutable sorted set from an async-enumerable sequence according to a specified comparer. + /// + /// The type of the elements in the source sequence. + /// The source async-enumerable sequence to get a hash set of elements for. + /// A comparer to compare elements of the sequence. + /// The optional cancellation token to be used for cancelling the sequence at any time. + /// An async-enumerable sequence containing a single element with a hash set containing all the elements of the source sequence. + /// is null. + /// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior. + public static ValueTask> ToImmutableSortedSetAsync(this IAsyncEnumerable source, IComparer? comparer, CancellationToken cancellationToken = default) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + + return source.ToCollection( + ImmutableSortedSet.CreateBuilder(comparer), + static builder => builder.ToImmutable(), + cancellationToken); + } + } +} diff --git a/Ix.NET/Source/System.Linq.Async/System/Linq/FunctionalHelpers.cs b/Ix.NET/Source/System.Linq.Async/System/Linq/FunctionalHelpers.cs new file mode 100644 index 0000000000..b080ab532e --- /dev/null +++ b/Ix.NET/Source/System.Linq.Async/System/Linq/FunctionalHelpers.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT License. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Linq +{ + internal static class FunctionalHelpers + { + public static T Identity(T value) => value; + + public static ValueTask IdentityAsync(T value) => new(value); + + public static ValueTask IdentityAsync(T value, CancellationToken token) => new(value); + + public static TKey Key(KeyValuePair kvp) => kvp.Key; + + public static TValue Value(KeyValuePair kvp) => kvp.Value; + } +} diff --git a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToHashSet.cs b/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToHashSet.cs index 2bf7c487f8..7590daf8f7 100644 --- a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToHashSet.cs +++ b/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToHashSet.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using static System.Linq.FunctionalHelpers; namespace System.Linq { @@ -37,19 +38,10 @@ public static ValueTask> ToHashSetAsync(this IAsyncEnu if (source == null) throw Error.ArgumentNull(nameof(source)); - return Core(source, comparer, cancellationToken); - - static async ValueTask> Core(IAsyncEnumerable source, IEqualityComparer? comparer, CancellationToken cancellationToken) - { - var set = new HashSet(comparer); - - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - set.Add(item); - } - - return set; - } + return source.ToCollection( + new HashSet(comparer), + Identity, + cancellationToken); } } } diff --git a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToList.cs b/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToList.cs index 6c50505d40..51ef03a36c 100644 --- a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToList.cs +++ b/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/ToList.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using static System.Linq.FunctionalHelpers; namespace System.Linq { @@ -27,19 +28,7 @@ public static ValueTask> ToListAsync(this IAsyncEnumerabl if (source is IAsyncIListProvider listProvider) return listProvider.ToListAsync(cancellationToken); - return Core(source, cancellationToken); - - static async ValueTask> Core(IAsyncEnumerable source, CancellationToken cancellationToken) - { - var list = new List(); - - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - list.Add(item); - } - - return list; - } + return source.ToCollection(new List(), Identity, cancellationToken); } } } diff --git a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Utilities.cs b/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Utilities.cs index a0073f1cbf..d2b1bda514 100644 --- a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Utilities.cs +++ b/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Utilities.cs @@ -43,5 +43,20 @@ public static async ValueTask AddRangeAsync(this List list, IAsyncEnumerab list.Add(item); } } + + public static async ValueTask ToCollection( + this IAsyncEnumerable source, + TCollection collection, + Func resultSelector, + CancellationToken cancellationToken) + where TCollection : ICollection + { + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + collection.Add(item); + } + + return resultSelector(collection); + } } } diff --git a/Ix.NET/Source/refs/System.Linq.Async.Ref/System.Linq.Async.Ref.csproj b/Ix.NET/Source/refs/System.Linq.Async.Ref/System.Linq.Async.Ref.csproj index ead9cd3018..0d355fff3c 100644 --- a/Ix.NET/Source/refs/System.Linq.Async.Ref/System.Linq.Async.Ref.csproj +++ b/Ix.NET/Source/refs/System.Linq.Async.Ref/System.Linq.Async.Ref.csproj @@ -16,6 +16,8 @@ ExtrasIsReferenceAssembly;AssemblyName;Version;AssemblyTitle + From 0bd36720fe154fb351b600009e6843fa0b0142ae Mon Sep 17 00:00:00 2001 From: Ian Griffiths Date: Wed, 28 Aug 2024 15:13:51 +0100 Subject: [PATCH 2/3] Add `System.Linq.Async` ToImmutable* to verified APIs --- ...iApprovalTests.SystemLinqAsync.verified.cs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/Ix.NET/Source/Tests.System.Interactive.ApiApprovals/Api/ApiApprovalTests.SystemLinqAsync.verified.cs b/Ix.NET/Source/Tests.System.Interactive.ApiApprovals/Api/ApiApprovalTests.SystemLinqAsync.verified.cs index 2734bde4bb..eee5b5cf0a 100644 --- a/Ix.NET/Source/Tests.System.Interactive.ApiApprovals/Api/ApiApprovalTests.SystemLinqAsync.verified.cs +++ b/Ix.NET/Source/Tests.System.Interactive.ApiApprovals/Api/ApiApprovalTests.SystemLinqAsync.verified.cs @@ -11,6 +11,93 @@ public static System.Threading.Tasks.ValueTask MoveNextAsync(this Syste public static System.Collections.Generic.IAsyncEnumerator WithCancellation(this System.Collections.Generic.IAsyncEnumerator source, System.Threading.CancellationToken cancellationToken) { } } } +namespace System.Collections.Immutable +{ + public static class ImmutableArrayAsyncEnumerableExtensions + { + public static System.Threading.Tasks.ValueTask> ToImmutableArrayAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Threading.CancellationToken cancellationToken = default) { } + } + public static class ImmutableDictionaryAsyncEnumerableExtensions + { + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable> source, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable> source, System.Collections.Generic.IEqualityComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func keySelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable> source, System.Collections.Generic.IEqualityComparer? keyComparer, System.Collections.Generic.IEqualityComparer? valueComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func keySelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func keySelector, System.Func elementSelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func keySelector, System.Func elementSelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func keySelector, System.Func elementSelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Collections.Generic.IEqualityComparer? valueComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Collections.Generic.IEqualityComparer? valueComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitWithCancellationAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitWithCancellationAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitWithCancellationAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitWithCancellationAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableDictionaryAwaitWithCancellationAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Collections.Generic.IEqualityComparer? keyComparer, System.Collections.Generic.IEqualityComparer? valueComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + } + public static class ImmutableHashSetAsyncEnumerableExtensions + { + public static System.Threading.Tasks.ValueTask> ToImmutableHashSetAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Threading.CancellationToken cancellationToken = default) { } + public static System.Threading.Tasks.ValueTask> ToImmutableHashSetAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Collections.Generic.IEqualityComparer? equalityComparer, System.Threading.CancellationToken cancellationToken = default) { } + } + public static class ImmutableListAsyncEnumerableExtensions + { + public static System.Threading.Tasks.ValueTask> ToImmutableListAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Threading.CancellationToken cancellationToken = default) { } + } + public static class ImmutableSortedDictionaryAsyncEnumerableExtensions + { + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable> source, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable> source, System.Collections.Generic.IComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable> source, System.Collections.Generic.IComparer? keyComparer, System.Collections.Generic.IEqualityComparer? valueComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func keySelector, System.Func elementSelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func keySelector, System.Func elementSelector, System.Collections.Generic.IComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func keySelector, System.Func elementSelector, System.Collections.Generic.IComparer? keyComparer, System.Collections.Generic.IEqualityComparer? valueComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAwaitAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAwaitAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Collections.Generic.IComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAwaitAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Collections.Generic.IComparer? keyComparer, System.Collections.Generic.IEqualityComparer? valueComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAwaitWithCancellationAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAwaitWithCancellationAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Collections.Generic.IComparer? keyComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedDictionaryAwaitWithCancellationAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Func> keySelector, System.Func> elementSelector, System.Collections.Generic.IComparer? keyComparer, System.Collections.Generic.IEqualityComparer? valueComparer, System.Threading.CancellationToken cancellationToken = default) + where TKey : notnull { } + } + public static class ImmutableSortedSetAsyncEnumerableExtensions + { + public static System.Threading.Tasks.ValueTask> ToImmutableSortedSetAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Threading.CancellationToken cancellationToken = default) { } + public static System.Threading.Tasks.ValueTask> ToImmutableSortedSetAsync(this System.Collections.Generic.IAsyncEnumerable source, System.Collections.Generic.IComparer? comparer, System.Threading.CancellationToken cancellationToken = default) { } + } +} namespace System.Linq { public static class AsyncEnumerable From e49c6b5005cf6d7f73e49508402f688d12fb74f0 Mon Sep 17 00:00:00 2001 From: Ian Griffiths Date: Wed, 28 Aug 2024 15:14:00 +0100 Subject: [PATCH 3/3] Add release notes --- Ix.NET/Documentation/ReleaseHistory/Ix.v6.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Ix.NET/Documentation/ReleaseHistory/Ix.v6.md b/Ix.NET/Documentation/ReleaseHistory/Ix.v6.md index 49ea8e79e0..c0c22a4d1f 100644 --- a/Ix.NET/Documentation/ReleaseHistory/Ix.v6.md +++ b/Ix.NET/Documentation/ReleaseHistory/Ix.v6.md @@ -1,6 +1,16 @@ # Ix Release History v6.0 +## v6.1.0 + +New functionality: + +* Added `ToImmutable*Async` extension methods so `System.Linq.Async` (PR https://github.com/dotnet/reactive/pull/1545 from [Tau Gärtli](https://github.com/bash)) + +Bug fixes: + + * Fix [Union dispose bug](https://github.com/dotnet/reactive/issues/2112) in `System.Linq.Async` + ## v6.0.1 First release with version number updated to v6.0.x. (At the time, Rx and Ix were attempting to follow a policy of keeping version numbers aligned with the .NET runtime libraries.)