Skip to content

Commit 960e063

Browse files
authored
Add {Async}Enumerable.{Infinite}Sequence (#116538)
* Add Enumerable.InfiniteSequence * Make RangeIterator generic * Add {Async}Enumerable.Sequence
1 parent 33ce341 commit 960e063

22 files changed

+1454
-236
lines changed

src/libraries/System.Linq.AsyncEnumerable/ref/System.Linq.AsyncEnumerable.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
<Compile Include="System.Linq.AsyncEnumerable.cs" />
88
</ItemGroup>
99

10+
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
11+
<Compile Include="SystemLinq.AsyncEnumerable.netcore.cs" />
12+
</ItemGroup>
13+
1014
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
1115
<ProjectReference Include="$(LibrariesProjectRoot)System.Linq/ref/System.Linq.csproj" />
1216
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime/ref/System.Runtime.csproj" />
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// ------------------------------------------------------------------------------
4+
// Changes to this file must follow the https://aka.ms/api-review process.
5+
// ------------------------------------------------------------------------------
6+
7+
namespace System.Linq
8+
{
9+
public static partial class AsyncEnumerable
10+
{
11+
public static System.Collections.Generic.IAsyncEnumerable<T> InfiniteSequence<T>(T start, T step) where T : System.Numerics.IAdditionOperators<T, T, T> { throw null; }
12+
public static System.Collections.Generic.IAsyncEnumerable<T> Sequence<T>(T start, T endInclusive, T step) where T : System.Numerics.INumber<T> { throw null; }
13+
}
14+
}

src/libraries/System.Linq.AsyncEnumerable/src/System.Linq.AsyncEnumerable.csproj

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
@@ -18,71 +18,76 @@
1818
</PropertyGroup>
1919

2020
<ItemGroup>
21-
<Compile Include="System\Linq\Shuffle.cs" />
22-
<Compile Include="System\Linq\SkipLast.cs" />
23-
<Compile Include="System\Linq\SkipWhile.cs" />
24-
<Compile Include="System\Linq\Append.cs" />
25-
<Compile Include="System\Linq\Empty.cs" />
26-
<Compile Include="System\Linq\MaxByAsync.cs" />
27-
<Compile Include="System\Linq\MinAsync.cs" />
28-
<Compile Include="System\Linq\Cast.cs" />
29-
<Compile Include="System\Linq\AsyncEnumerable.cs" />
30-
<Compile Include="System\Linq\TakeLast.cs" />
31-
<Compile Include="System\Linq\TakeWhile.cs" />
32-
<Compile Include="System\Linq\UnionBy.cs" />
3321
<Compile Include="System\Linq\AggregateAsync.cs" />
34-
<Compile Include="System\Linq\AnyAsync.cs" />
22+
<Compile Include="System\Linq\AggregateBy.cs" />
3523
<Compile Include="System\Linq\AllAsync.cs" />
36-
<Compile Include="System\Linq\Prepend.cs" />
24+
<Compile Include="System\Linq\AnyAsync.cs" />
25+
<Compile Include="System\Linq\Append.cs" />
26+
<Compile Include="System\Linq\AsyncEnumerable.cs" />
3727
<Compile Include="System\Linq\AverageAsync.cs" />
28+
<Compile Include="System\Linq\Cast.cs" />
3829
<Compile Include="System\Linq\Chunk.cs" />
3930
<Compile Include="System\Linq\Concat.cs" />
4031
<Compile Include="System\Linq\ContainsAsync.cs" />
41-
<Compile Include="System\Linq\AggregateBy.cs" />
42-
<Compile Include="System\Linq\CountBy.cs" />
4332
<Compile Include="System\Linq\CountAsync.cs" />
33+
<Compile Include="System\Linq\CountBy.cs" />
4434
<Compile Include="System\Linq\DefaultIfEmpty.cs" />
45-
<Compile Include="System\Linq\DistinctBy.cs" />
4635
<Compile Include="System\Linq\Distinct.cs" />
36+
<Compile Include="System\Linq\DistinctBy.cs" />
4737
<Compile Include="System\Linq\ElementAtAsync.cs" />
48-
<Compile Include="System\Linq\ToAsyncEnumerable.cs" />
49-
<Compile Include="System\Linq\ExceptBy.cs" />
38+
<Compile Include="System\Linq\Empty.cs" />
5039
<Compile Include="System\Linq\Except.cs" />
40+
<Compile Include="System\Linq\ExceptBy.cs" />
5141
<Compile Include="System\Linq\FirstAsync.cs" />
5242
<Compile Include="System\Linq\GroupBy.cs" />
5343
<Compile Include="System\Linq\GroupJoin.cs" />
5444
<Compile Include="System\Linq\Index.cs" />
55-
<Compile Include="System\Linq\IntersectBy.cs" />
5645
<Compile Include="System\Linq\Intersect.cs" />
46+
<Compile Include="System\Linq\IntersectBy.cs" />
5747
<Compile Include="System\Linq\Join.cs" />
5848
<Compile Include="System\Linq\LastAsync.cs" />
5949
<Compile Include="System\Linq\LeftJoin.cs" />
60-
<Compile Include="System\Linq\ToArrayAsync.cs" />
61-
<Compile Include="System\Linq\ToDictionaryAsync.cs" />
62-
<Compile Include="System\Linq\ToListAsync.cs" />
63-
<Compile Include="System\Linq\ToLookupAsync.cs" />
6450
<Compile Include="System\Linq\MaxAsync.cs" />
51+
<Compile Include="System\Linq\MaxByAsync.cs" />
52+
<Compile Include="System\Linq\MinAsync.cs" />
6553
<Compile Include="System\Linq\MinByAsync.cs" />
6654
<Compile Include="System\Linq\OfType.cs" />
6755
<Compile Include="System\Linq\OrderBy.cs" />
56+
<Compile Include="System\Linq\Prepend.cs" />
6857
<Compile Include="System\Linq\Range.cs" />
6958
<Compile Include="System\Linq\Repeat.cs" />
7059
<Compile Include="System\Linq\Reverse.cs" />
7160
<Compile Include="System\Linq\RightJoin.cs" />
7261
<Compile Include="System\Linq\Select.cs" />
7362
<Compile Include="System\Linq\SelectMany.cs" />
7463
<Compile Include="System\Linq\SequenceEqualAsync.cs" />
64+
<Compile Include="System\Linq\Shuffle.cs" />
7565
<Compile Include="System\Linq\SingleAsync.cs" />
7666
<Compile Include="System\Linq\Skip.cs" />
67+
<Compile Include="System\Linq\SkipLast.cs" />
68+
<Compile Include="System\Linq\SkipWhile.cs" />
7769
<Compile Include="System\Linq\SumAsync.cs" />
7870
<Compile Include="System\Linq\Take.cs" />
71+
<Compile Include="System\Linq\TakeLast.cs" />
72+
<Compile Include="System\Linq\TakeWhile.cs" />
7973
<Compile Include="System\Linq\ThrowHelper.cs" />
74+
<Compile Include="System\Linq\ToArrayAsync.cs" />
75+
<Compile Include="System\Linq\ToAsyncEnumerable.cs" />
76+
<Compile Include="System\Linq\ToDictionaryAsync.cs" />
8077
<Compile Include="System\Linq\ToHashSetAsync.cs" />
78+
<Compile Include="System\Linq\ToListAsync.cs" />
79+
<Compile Include="System\Linq\ToLookupAsync.cs" />
8180
<Compile Include="System\Linq\Union.cs" />
81+
<Compile Include="System\Linq\UnionBy.cs" />
8282
<Compile Include="System\Linq\Where.cs" />
8383
<Compile Include="System\Linq\Zip.cs" />
8484
</ItemGroup>
8585

86+
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
87+
<Compile Include="System\Linq\InfiniteSequence.cs" />
88+
<Compile Include="System\Linq\Sequence.cs" />
89+
</ItemGroup>
90+
8691
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
8792
<Reference Include="System.Collections" />
8893
<Reference Include="System.Linq" />
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Numerics;
7+
using System.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
9+
10+
namespace System.Linq
11+
{
12+
public static partial class AsyncEnumerable
13+
{
14+
/// <summary>Generates an infinite sequence that begins with <paramref name="start"/> and yields additional values each incremented by <paramref name="step"/>.</summary>
15+
/// <typeparam name="T">The type of the value to be yielded in the result sequence.</typeparam>
16+
/// <param name="start">The starting value.</param>
17+
/// <param name="step">The amount by which the next yielded value should be incremented from the previous yielded value.</param>
18+
/// <returns>An <see cref="IAsyncEnumerable{T}"/> that contains the sequence.</returns>
19+
public static IAsyncEnumerable<T> InfiniteSequence<T>(T start, T step) where T : IAdditionOperators<T, T, T>
20+
{
21+
if (start is null)
22+
{
23+
ThrowHelper.ThrowArgumentNullException(nameof(start));
24+
}
25+
26+
if (step is null)
27+
{
28+
ThrowHelper.ThrowArgumentNullException(nameof(step));
29+
}
30+
31+
return Iterator(start, step);
32+
33+
static async IAsyncEnumerable<T> Iterator(T start, T step)
34+
{
35+
while (true)
36+
{
37+
yield return start;
38+
start += step;
39+
}
40+
}
41+
}
42+
}
43+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Numerics;
7+
using System.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
9+
10+
namespace System.Linq
11+
{
12+
public static partial class AsyncEnumerable
13+
{
14+
/// <summary>Generates a sequence that begins with <paramref name="start"/> and yields additional values each incremented by <paramref name="step"/> until <paramref name="endInclusive"/> is reached.</summary>
15+
/// <typeparam name="T">The type of the value to be yielded in the result sequence.</typeparam>
16+
/// <param name="start">The starting value. This value will always be included in the resulting sequence.</param>
17+
/// <param name="endInclusive">The ending bound beyond which values will not be included in the sequence.</param>
18+
/// <param name="step">The amount by which the next value in the sequence should be incremented from the previous value.</param>
19+
/// <returns>An <see cref="IAsyncEnumerable{T}"/> that contains the sequence.</returns>
20+
/// <exception cref="ArgumentNullException"><paramref name="start"/> is <see langword="null"/>.</exception>
21+
/// <exception cref="ArgumentNullException"><paramref name="step"/> is <see langword="null"/>.</exception>
22+
/// <exception cref="ArgumentNullException"><paramref name="endInclusive"/> is <see langword="null"/>.</exception>
23+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="step"/> is greater than zero but <paramref name="endInclusive"/> is less than <paramref name="start"/>.</exception>
24+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="step"/> is less than zero but <paramref name="endInclusive"/> is greater than <paramref name="start"/>.</exception>
25+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="step"/> is zero and <paramref name="endInclusive"/> does not equal <paramref name="start"/>.</exception>
26+
public static IAsyncEnumerable<T> Sequence<T>(T start, T endInclusive, T step) where T : INumber<T>
27+
{
28+
if (start is null)
29+
{
30+
ThrowHelper.ThrowArgumentNullException(nameof(start));
31+
}
32+
33+
if (endInclusive is null)
34+
{
35+
ThrowHelper.ThrowArgumentNullException(nameof(endInclusive));
36+
}
37+
38+
if (step is null)
39+
{
40+
ThrowHelper.ThrowArgumentNullException(nameof(step));
41+
}
42+
43+
if (step > T.Zero)
44+
{
45+
// Presumed to be the most common case, step > 0. Validate that endInclusive >= start, as otherwise we can't easily
46+
// guarantee that the sequence will terminate.
47+
if (endInclusive < start)
48+
{
49+
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(endInclusive));
50+
}
51+
52+
// Otherwise, just produce an incrementing sequence.
53+
return IncrementingIterator(start, endInclusive, step);
54+
}
55+
else if (step < T.Zero)
56+
{
57+
// step < 0. Validate that endInclusive <= start, as otherwise we can't easily guarantee that the sequence will terminate.
58+
if (endInclusive > start)
59+
{
60+
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(endInclusive));
61+
}
62+
63+
// Then produce the decrementing sequence.
64+
return DecrementingIterator(start, endInclusive, step);
65+
}
66+
else
67+
{
68+
// step == 0. If start != endInclusive, then the sequence would be infinite. As such, we validate
69+
// that they're equal, and if they are, we return a sequence that yields the start/endInclusive value once.
70+
if (start != endInclusive)
71+
{
72+
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(step));
73+
}
74+
75+
return Repeat(start, 1);
76+
}
77+
78+
static async IAsyncEnumerable<T> IncrementingIterator(T start, T endInclusive, T step)
79+
{
80+
Debug.Assert(step > T.Zero);
81+
82+
yield return start;
83+
84+
while (true)
85+
{
86+
T next = start + step;
87+
88+
if (next >= endInclusive || next <= start)
89+
{
90+
if (next == endInclusive && start != next)
91+
{
92+
yield return next;
93+
}
94+
95+
yield break;
96+
}
97+
98+
yield return next;
99+
start = next;
100+
}
101+
}
102+
103+
104+
static async IAsyncEnumerable<T> DecrementingIterator(T start, T endInclusive, T step)
105+
{
106+
Debug.Assert(step < T.Zero);
107+
108+
yield return start;
109+
110+
while (true)
111+
{
112+
T next = start + step;
113+
114+
if (next <= endInclusive || next >= start)
115+
{
116+
if (next == endInclusive && start != next)
117+
{
118+
yield return next;
119+
}
120+
121+
yield break;
122+
}
123+
124+
yield return next;
125+
start = next;
126+
}
127+
}
128+
}
129+
}
130+
}

src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ThrowHelper.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ internal static void ThrowIfNegativeOrZero(int value, [CallerArgumentExpression(
2424
}
2525
}
2626

27+
[DoesNotReturn]
28+
internal static void ThrowArgumentNullException(string paramName) => throw new ArgumentNullException(paramName);
29+
2730
[DoesNotReturn]
2831
internal static void ThrowArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName);
2932

0 commit comments

Comments
 (0)