I was playing around with C# 8.0 preview and can't get IAsyncEnumerable
to work.
I tried the following
public static async IAsyncEnumerable<int> Get()
{
for(int i=0; i<10; i++)
{
await Task.Delay(100);
yield return i;
}
}
I ended up using a Nuget package named AsyncEnumerator
, but I'm getting the following error:
- Error CS1061 '
IAsyncEnumerable<int>
' does not contain a definition for 'GetAwaiter
' and no accessible extension method 'GetAwaiter
' accepting a first argument of type 'IAsyncEnumerable<int>
' could be found (are you missing a using directive or an assembly reference?) - Error CS1624 The body of '
Program.Get()
' cannot be an iterator block because 'IAsyncEnumerable<int>
' is not an iterator interface type
What am I missing here?
Regarding the bridging code needed to make Async enumerables work, I published a NuGet a couple of days ago that does just that: CSharp8Beta.AsyncIteratorPrerequisites.Unofficial
Contrary to popular belief, the following code actually produces the expected results:
and that is because the
IEnumerable<int>
is being materialized into anint
array. What would actually terminate after two iterations is iterating over theIEnumerable<int>
itself like so:Still, while turning queries into materialized collections might seem like a clever trick, it isn't always the case that you would like to buffer the entire sequence (thus losing both memory and time).
With performance in mind, what I found is that an almost zero allocating wrapper over the
IEnumerable
which would turn it into anIAsyncEnumerable
plus usingawait foreach
instead of justforeach
would circumvent the issue.I have recently published a new version of the NuGet package which now includes an extension method called
ToAsync<T>()
forIEnumerable<T>
in general, placed inSystem.Collections.Generic
which does just that. The method's signature is:and upon adding the NuGet package to a .NET Core 3 project one could use it like so:
Notice the two changes:
foreach
becomesawait foreach
nums
becomsnums.ToAsync()
The wrapper is as lightweight as possible and its implementation is based on the following classes (note that the using of
ValueTask<T>
as enforced by theIAsyncEnumerable<T>
andIAsyncEnumerator<T>
allows for a constant number of Heap allocations perforeach
):To sum it up:
To be able to write async generator methods (
async IAsyncEnumerable<int> MyMethod() ...
) and to consume async enumerables (await foreach (var x in ...
) simply install the NuGet in your project.In order to also circumvent the iteration premature stop, make sure you've got
System.Collections.Generic
in yourusing
clauses, call.ToAsync()
on yourIEnumerable
and turn yourforeach
into anawait foreach
.That's a bug in the compiler that can be fixed by adding a few lines of code found here :
As Mads Torgersen explains in Take C# 8 for a spin :
Update
Looks there's another bug when
Enumerable.Range()
is used inside the async iterator.The
GetNumbersAsync()
method in the issue ends after only two iterations :This will print only :
This won't happen with an array or even another iterator method:
This will print the expected :
Update 2
Seems that's a know bug as well: Async-Streams: iteration stops early on Core