Search code examples
c#iasyncenumerableiasyncdisposable

Correct disposal in IAsyncEnumerable methods?


The answer to this may be that it's not possible, but the question is: assume you have a C# method for consuming the lines in a TextReader that returns IAsyncEnumerable<string>. How do you ensure that when DisposeAsync is called on the IAsyncEnumerator<string> that the TextReader is disposed of? Or is this something you need to write a custom implementation to achieve?


Solution

  • Just use a try-finally block inside the async iterator, or more simply a using block, and the TextReader will be disposed as soon as the caller completes the enumeration. It doesn't matter if the enumeration will complete normally, or prematurely because of an exception or a break.

    static async Task Main(string[] args)
    {
        await foreach (var line in GetLines())
        {
            Console.WriteLine(line);
        }
    }
    
    private static async IAsyncEnumerable<string> GetLines()
    {
        var reader = new StringReader("Line1\nLine2\nLine3");
        try
        {
            while (true)
            {
                var line = await reader.ReadLineAsync();
                if (line == null) break;
                yield return line;
            }
        }
        finally
        {
            reader.Dispose();
            Console.WriteLine("Disposed");
        }
    }
    

    Output:

    Line1
    Line2
    Line3
    Disposed