Search code examples
c#asynchronousasync-awaitcollection-initializer

Asynchronous population of collection initializer


I would like to populate a collection using collection initializer that will call async methods:

public class Diagnostics
{
    public async Task<IEnumerable<DiagnosticsInfo>> Get() => new List<DiagnosticsInfo>
    {            
        await GetDiagnosticsInfo1(),
        await GetDiagnosticsInfo2(),
        await GetDiagnosticsInfo3()
    };
}

The aim is for all the GetDiagnosticsInfoX() methods to execute in parallel. I have a suspicion that the generated code will invoke GetDiagnosticsInfoX() methods synchronously - it will await the first call before invoking the second.

Is my gut feel correct?

If so I assume I would need to await all the tasks like this:

public class Diagnostics
{
    public async Task<IEnumerable<DiagnosticsInfo>> Get()
    {
        var task1 = GetDiagnosticsInfo1();
        var task2 = GetDiagnosticsInfo2();
        var task3 = GetDiagnosticsInfo3();

        await Task.WhenAll(task1, task2, task3);

        return new List<DiagnosticsInfo> {task1.Result, task2.Result, task3.Result};
    }
}

Solution

  • Is my gut feel correct?

    Your gut feeling is right. All collection initializer does is invoke the Add method for the collection. This means your code is translated to:

    public async Task<IEnumerable<DiagnosticsInfo>> Get()
    {
        var list = new List<DiagnosticsInfo>();         
        list.Add(await GetDiagnosticsInfo1());
        list.Add(await GetDiagnosticsInfo2());
        list.Add(await GetDiagnosticsInfo3());
        return list;
    }
    

    Using Task.WhenAll will wait for all async methods to complete. You can then return the array you receive:

    public async Task<IEnumerable<DiagnosticsInfo>> Get()
    {
        var task1 = GetDiagnosticsInfo1();
        var task2 = GetDiagnosticsInfo2();
        var task3 = GetDiagnosticsInfo3();
    
        return await Task.WhenAll(task1, task2, task3);
    }