How to return Yield Results in Async Operations

Programming LINQ C#

Async Yield Return thanks to IAsyncEnumerable

General description of yield return:

When you use the yield return statement in your code, you indicate that the method, operator, or get accessor in which the yield return keyword appears is an iterator. The yield return statement will return one element in a collection at a time.

The result returned from a yield return iterator method can be consumed by using a foreach statement or LINQ query. Each iteration of the foreach loop calls the iterator method. When a yield return statement is reached in the iterator method, the result is returned and the current location in the iterator method is retained. Upon the execution of the next element in the foreach loop, the iterator method is restarted from the retained location the next time that the iterator method is called.

Standard yield Example:

The following code

IEnumerable<int> GenerateWithoutYield() 
{
var i = 0;
var list = new List<int>();
while (i<5)
list.Add(++i);
return list;
}

foreach(var number in GenerateWithoutYield())
Console.WriteLine(number);

can be refactored to make use of the yield return statement as follows:

IEnumerable<int> GenerateWithYield() 
{
var i = 0;
while (i<5)
yield return ++i;
}

foreach(var number in GenerateWithYield())
Console.WriteLine(number);

Async yield return:

When working with asynchronous code using async/await, the use of the yield return statement requires just a few small adjustments to your code. Using an async yield return statement requires that the method be asynchronous, making use of async/await.

Usually an async method will return a task. Your first thought when using yield return in your async method may be to have the method return Task of IEnumerable. However, this is not allowed when using yield return, instead your async yield return method must return an IAsyncEnumerable.

You also cannot await your new async yield return method that returns an IAsynEnumerable. Instead, yo must await the foreach statement instead. This is becuase IAsyncEnumerable does not have a GetAwaiter() method. IAsyncEnumerable is an async iterator.

Example Code:

private async void ReadTheFile()
{
var path = Path.Combine(Environment.CurrentDirectory, "file.txt");
var lines = GetTxtLinesFromFile(path);

// do something with read-in lines
await foreach (var line in lines)
_listBox.Items.Add(line);
}

async IAsyncEnumerable<string> GetTxtLinesFromFile(string filePath)
{
string line;
StreamReader file = new System.IO.StreamReader(filePath);

while ((line = await file.ReadLineAsync()) != null)
yield return line;
}