Skip to content

Commit e695dd9

Browse files
committed
Improved type inference for SelectManyAsync
- Adds overrides to SelectManyAsync to better support type inference with List<T>, T[] and IEnumerable<T> for both IEnumerable<T> and IAsyncEnumerable<T>, and for variants with or without asynchronous resultSelector - Unit tests have been renamed for greater clarity and enriched to cover these new use cases
1 parent b72a970 commit e695dd9

2 files changed

Lines changed: 141 additions & 27 deletions

File tree

src/AsyncLinqR.Tests/SelectManyAsyncTest.cs

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,73 +4,86 @@ namespace AsyncLinqR.Tests;
44

55
public class SelectManyAsyncTest
66
{
7-
// TODO Renommer tous les tests pour donner des noms parlants
87
[Fact]
9-
public async Task Suite()
8+
public async Task SelectManyAsync_on_IEnumerable_should_enable_type_inference_without_having_to_specify_generic_types()
109
{
1110
var enumerable = await Fake<IEnumerable<int>>.Create([]).SelectManyAsync(x => x.List).ToListAsync();
1211
enumerable.Should().BeOfType<List<int>>();
1312

1413
var array = await Fake<int[]>.Create([]).SelectManyAsync(x => x.List).ToListAsync();
1514
array.Should().BeOfType<List<int>>();
16-
17-
// TODO Rajouter List<int>
15+
16+
var list = await Fake<List<int>>.Create([]).SelectManyAsync(x => x.List).ToListAsync();
17+
list.Should().BeOfType<List<int>>();
1818
}
1919

2020
[Fact]
21-
public async Task SelectAsync_patin_couffin()
21+
public async Task SelectManyAsync_on_IAsyncEnumerable_should_enable_type_inference_without_having_to_specify_generic_types()
2222
{
2323
var enumerable = await Fake<IEnumerable<int>>.CreateAsync([]).SelectManyAsync(x => x.List).ToListAsync();
2424
enumerable.Should().BeOfType<List<int>>();
2525

2626
var array = await Fake<int[]>.CreateAsync([]).SelectManyAsync(x => x.List).ToListAsync();
2727
array.Should().BeOfType<List<int>>();
2828

29-
// TODO Rajouter List<int>
29+
var list = await Fake<List<int>>.CreateAsync([]).SelectManyAsync(x => x.List).ToListAsync();
30+
list.Should().BeOfType<List<int>>();
3031
}
3132

3233
[Fact]
33-
public async Task Selector_and_async_changer_ces_noms_pourris()
34+
public async Task SelectManyAsync_on_IEnumerable_with_selector_should_enable_type_inference_without_having_to_specify_generic_types()
3435
{
35-
var enumerable = await Fake<IEnumerable<int>>.CreateAsync([]).SelectManyAsync(x => x.List, (y, i) => i).ToListAsync();
36+
var enumerable = await Fake<IEnumerable<int>>.Create([]).SelectManyAsync(x => x.List, (y, i) => i).ToListAsync();
3637
enumerable.Should().BeOfType<List<int>>();
3738

38-
// TODO Rajouter int[]
39-
// TODO Rajouter List<int>
39+
var array = await Fake<int[]>.Create([]).SelectManyAsync(x => x.List, (y, i) => i).ToListAsync();
40+
array.Should().BeOfType<List<int>>();
41+
42+
var list = await Fake<List<int>>.Create([]).SelectManyAsync(x => x.List, (y, i) => i).ToListAsync();
43+
list.Should().BeOfType<List<int>>();
4044
}
4145

4246
[Fact]
43-
public async Task Selector_and_sync_changer_ces_noms_pourris()
47+
public async Task SelectManyAsync_on_IAsyncEnumerable_with_selector_should_enable_type_inference_without_having_to_specify_generic_types()
4448
{
45-
var enumerable = await Fake<IEnumerable<int>>.Create([]).SelectManyAsync(x => x.List, (y, i) => i).ToListAsync();
49+
var enumerable = await Fake<IEnumerable<int>>.CreateAsync([]).SelectManyAsync(x => x.List, (y, i) => i).ToListAsync();
4650
enumerable.Should().BeOfType<List<int>>();
4751

48-
// TODO Rajouter int[]
49-
// TODO Rajouter List<int>
52+
var array = await Fake<int[]>.CreateAsync([]).SelectManyAsync(x => x.List, (y, i) => i).ToListAsync();
53+
array.Should().BeOfType<List<int>>();
54+
55+
var list = await Fake<List<int>>.CreateAsync([]).SelectManyAsync(x => x.List, (y, i) => i).ToListAsync();
56+
list.Should().BeOfType<List<int>>();
5057
}
5158

5259
[Fact]
53-
public async Task Selector_and_async_changer_ces_noms_pourris_toto()
60+
public async Task SelectManyAsync_on_IEnumerable_with_async_selector_should_enable_type_inference_without_having_to_specify_generic_types()
5461
{
55-
var enumerable = await Fake<IEnumerable<int>>.CreateAsync([]).SelectManyAsync(x => x.List, (y, i) => Task<int>.Factory.StartNew(() => i)).ToListAsync();
62+
var enumerable = await Fake<IEnumerable<int>>.Create([]).SelectManyAsync(x => x.List, (y, i) => Task<int>.Factory.StartNew(() => i)).ToListAsync();
5663
enumerable.Should().BeOfType<List<int>>();
5764

58-
// TODO Rajouter int[]
59-
// TODO Rajouter List<int>
65+
var array = await Fake<int[]>.Create([]).SelectManyAsync(x => x.List, (y, i) => Task<int>.Factory.StartNew(() => i)).ToListAsync();
66+
array.Should().BeOfType<List<int>>();
67+
68+
var list = await Fake<List<int>>.Create([]).SelectManyAsync(x => x.List, (y, i) => Task<int>.Factory.StartNew(() => i)).ToListAsync();
69+
list.Should().BeOfType<List<int>>();
6070
}
6171

6272
[Fact]
63-
public async Task Selector_and_sync_changer_ces_noms_pourris_toto()
73+
public async Task SelectManyAsync_on_IAsyncEnumerable_with_async_selector_should_enable_type_inference_without_having_to_specify_generic_types()
6474
{
65-
var enumerable = await Fake<IEnumerable<int>>.Create([]).SelectManyAsync(x => x.List, (y, i) => Task<int>.Factory.StartNew(() => i)).ToListAsync();
75+
var enumerable = await Fake<IEnumerable<int>>.CreateAsync([]).SelectManyAsync(x => x.List, (y, i) => Task<int>.Factory.StartNew(() => i)).ToListAsync();
6676
enumerable.Should().BeOfType<List<int>>();
6777

68-
// TODO Rajouter int[]
69-
// TODO Rajouter List<int>
78+
var array = await Fake<int[]>.CreateAsync([]).SelectManyAsync(x => x.List, (y, i) => Task<int>.Factory.StartNew(() => i)).ToListAsync();
79+
array.Should().BeOfType<List<int>>();
80+
81+
var list = await Fake<List<int>>.CreateAsync([]).SelectManyAsync(x => x.List, (y, i) => Task<int>.Factory.StartNew(() => i)).ToListAsync();
82+
list.Should().BeOfType<List<int>>();
7083
}
7184
}
7285

73-
record Fake<T>(int Value, Task<T> List)
86+
file record Fake<T>(int Value, Task<T> List)
7487
{
7588
public static IEnumerable<Fake<T>> Create(T list)
7689
{

src/AsyncLinqR/SelectManyAsync.cs

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
public static partial class AsyncLinq
44
{
5-
// TODO Reprendre tous les appels avec IEnumerable<TResult> et les dupliquer avec TResult[] et List<TResult>
6-
// TODO Faire un test qui garantit qu'on n'ait pas d'ambiguité sur les appels sans devoir spécifier les types génériques
7-
// TODO Valider qu'on est bien avec List et array
8-
95
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TResult>(this IAsyncEnumerable<T> source, Func<T, IEnumerable<TResult>> selector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
106
{
117
await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
@@ -38,6 +34,17 @@ public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TResult>(this I
3834
}
3935
}
4036

37+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TResult>(this IEnumerable<T> source, Func<T, Task<List<TResult>>> selector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
38+
{
39+
cancellationToken.ThrowIfCancellationRequested();
40+
foreach (var item in source)
41+
foreach (var result in await selector(item).ConfigureAwait(false))
42+
{
43+
cancellationToken.ThrowIfCancellationRequested();
44+
yield return result;
45+
}
46+
}
47+
4148
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TResult>(this IAsyncEnumerable<T> source, Func<T, Task<IEnumerable<TResult>>> selector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
4249
{
4350
await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
@@ -58,6 +65,16 @@ public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TResult>(this I
5865
}
5966
}
6067

68+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TResult>(this IAsyncEnumerable<T> source, Func<T, Task<List<TResult>>> selector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
69+
{
70+
await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
71+
foreach (var result in await selector(item).ConfigureAwait(false))
72+
{
73+
cancellationToken.ThrowIfCancellationRequested();
74+
yield return result;
75+
}
76+
}
77+
6178
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TResult>(this IEnumerable<T> source, Func<T, IAsyncEnumerable<TResult>> selector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
6279
{
6380
cancellationToken.ThrowIfCancellationRequested();
@@ -93,6 +110,26 @@ public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TR
93110
}
94111
}
95112

113+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IAsyncEnumerable<T> source, Func<T, Task<TCollection[]>> collectionSelector, Func<T, TCollection, TResult> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
114+
{
115+
await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
116+
foreach (var result in await collectionSelector(item).ConfigureAwait(false))
117+
{
118+
cancellationToken.ThrowIfCancellationRequested();
119+
yield return resultSelector(item, result);
120+
}
121+
}
122+
123+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IAsyncEnumerable<T> source, Func<T, Task<List<TCollection>>> collectionSelector, Func<T, TCollection, TResult> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
124+
{
125+
await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
126+
foreach (var result in await collectionSelector(item).ConfigureAwait(false))
127+
{
128+
cancellationToken.ThrowIfCancellationRequested();
129+
yield return resultSelector(item, result);
130+
}
131+
}
132+
96133
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IEnumerable<T> source, Func<T, Task<IEnumerable<TCollection>>> collectionSelector, Func<T, TCollection, TResult> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
97134
{
98135
cancellationToken.ThrowIfCancellationRequested();
@@ -104,6 +141,28 @@ public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TR
104141
}
105142
}
106143

144+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IEnumerable<T> source, Func<T, Task<TCollection[]>> collectionSelector, Func<T, TCollection, TResult> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
145+
{
146+
cancellationToken.ThrowIfCancellationRequested();
147+
foreach (var item in source)
148+
foreach (var result in await collectionSelector(item).ConfigureAwait(false))
149+
{
150+
cancellationToken.ThrowIfCancellationRequested();
151+
yield return resultSelector(item, result);
152+
}
153+
}
154+
155+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IEnumerable<T> source, Func<T, Task<List<TCollection>>> collectionSelector, Func<T, TCollection, TResult> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
156+
{
157+
cancellationToken.ThrowIfCancellationRequested();
158+
foreach (var item in source)
159+
foreach (var result in await collectionSelector(item).ConfigureAwait(false))
160+
{
161+
cancellationToken.ThrowIfCancellationRequested();
162+
yield return resultSelector(item, result);
163+
}
164+
}
165+
107166
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IEnumerable<T> source, Func<T, IAsyncEnumerable<TCollection>> collectionSelector, Func<T, TCollection, TResult> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
108167
{
109168
cancellationToken.ThrowIfCancellationRequested();
@@ -150,6 +209,26 @@ public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TR
150209
}
151210
}
152211

212+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IAsyncEnumerable<T> source, Func<T, Task<TCollection[]>> collectionSelector, Func<T, TCollection, Task<TResult>> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
213+
{
214+
await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
215+
foreach (var result in await collectionSelector(item).ConfigureAwait(false))
216+
{
217+
cancellationToken.ThrowIfCancellationRequested();
218+
yield return await resultSelector(item, result).ConfigureAwait(false);
219+
}
220+
}
221+
222+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IAsyncEnumerable<T> source, Func<T, Task<List<TCollection>>> collectionSelector, Func<T, TCollection, Task<TResult>> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
223+
{
224+
await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false))
225+
foreach (var result in await collectionSelector(item).ConfigureAwait(false))
226+
{
227+
cancellationToken.ThrowIfCancellationRequested();
228+
yield return await resultSelector(item, result).ConfigureAwait(false);
229+
}
230+
}
231+
153232
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IEnumerable<T> source, Func<T, Task<IEnumerable<TCollection>>> collectionSelector, Func<T, TCollection, Task<TResult>> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
154233
{
155234
cancellationToken.ThrowIfCancellationRequested();
@@ -161,6 +240,28 @@ public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TR
161240
}
162241
}
163242

243+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IEnumerable<T> source, Func<T, Task<TCollection[]>> collectionSelector, Func<T, TCollection, Task<TResult>> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
244+
{
245+
cancellationToken.ThrowIfCancellationRequested();
246+
foreach (var item in source)
247+
foreach (var result in await collectionSelector(item).ConfigureAwait(false))
248+
{
249+
cancellationToken.ThrowIfCancellationRequested();
250+
yield return await resultSelector(item, result).ConfigureAwait(false);
251+
}
252+
}
253+
254+
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IEnumerable<T> source, Func<T, Task<List<TCollection>>> collectionSelector, Func<T, TCollection, Task<TResult>> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
255+
{
256+
cancellationToken.ThrowIfCancellationRequested();
257+
foreach (var item in source)
258+
foreach (var result in await collectionSelector(item).ConfigureAwait(false))
259+
{
260+
cancellationToken.ThrowIfCancellationRequested();
261+
yield return await resultSelector(item, result).ConfigureAwait(false);
262+
}
263+
}
264+
164265
public static async IAsyncEnumerable<TResult> SelectManyAsync<T, TCollection, TResult>(this IEnumerable<T> source, Func<T, IAsyncEnumerable<TCollection>> collectionSelector, Func<T, TCollection, Task<TResult>> resultSelector, [EnumeratorCancellation] CancellationToken cancellationToken = default)
165266
{
166267
cancellationToken.ThrowIfCancellationRequested();

0 commit comments

Comments
 (0)