Cannot implicitly convert type 'void' to 'System.Threading.Tasks.Task'

15,508

Solution 1

Since you are trying to store the results of all your calls to GetDataAsync which are tasks, you shouldn't be awaiting them. Simply remove the await, collect all the tasks and wait for them at the end.

static void Run()
{
    List<string> IDs = new List<string>() { "a", "b", "c", "d", "e", "f" };
    Task[] tasks = new Task[IDs.Count];
    for (int i = 0; i < IDs.Count; i++)
        tasks[i] = GetDataAsync(IDs[i]);

    Task.WaitAll(tasks);    
}

Also, there's no need to make Run async at all (especially not async void which should only be used for UI event handlers) since you are waiting synchronously with Task.WaitAll.

If you want to wait asynchronously then make Run async void (only if it's a UI event handler) and use Task.WhenAll(tasks) to wait all tasks together:

static async void Run()
{
    await Task.WhenAll(new[] {"a", "b", "c", "d", "e", "f"}.Select(GetDataAsync));
}

Solution 2

The return value of an async function is Task instead of void and Task<TResult> instead of TResult.

If you change your function into the following it will be correct:

private static async Task Run()
{
    ...
    await ...
}

There is one exception, an async event handler may return void:

private async void OnButton1_clicked(object sender, ...)
{
    await Run();
}

This is enough to keep your UI responsive while the task is running.

  • You can only use await in a function if your function is async
  • async functions return Task instead of void and Task<TResult> instead of TResult.
    • the value of await Task<TResult>, is TResult.
    • only async functions can all other async functions.

If you have a non-async function and you want to start a task while doing other things, you can use the following code:

private void MyFunction()
{
    // do some processing
    // start a separate task, don't wait for it to finish:
    var myTask = Task.Run( () => MyAsyncFunction(...))
    // while the task is running you can do other things
    // after a while you need the result:
    TResult result = await myTask;
    ProcessResult(result);
}

You can even start several tasks, and while they are processing do other things and after a while wait for the tasks to finish:

private async void OnButton1_clicked(object sender, ...)
{
    var Tasks = new List<Task>();
    for (int i=0; i<10; ++i)
    {
        Tasks.Add(Run());
    }
    // while all tasks are scheduled to run, do other things
    // after a while you need the result:
    await Task.WhenAll(tasks);
    // Task.WhenAll(...) returns a Task, so await Task.WhenAll returns void
    // async functions that return Task`<TResult`> has a Result property 
    // that has the TResult value when the task if finished:
    foreach (var task in tasks)
    {
        ProcessResult(task.Result);
    }
    // not valid for your Run(), because that had a Task return value.
}
Share:
15,508

Related videos on Youtube

crazy novice
Author by

crazy novice

Updated on June 30, 2022

Comments

  • crazy novice
    crazy novice over 1 year

    Here is simplified version of my code below which is generating following compilation error

    Cannot implicitly convert type 'void' to 'System.Threading.Tasks.Task'

    GetDataAsync method does not need to return anything in this case. How can I make it return a Task that I can wait on?

    static async void Run()
    {
        List<string> IDs = new List<string>() { "a", "b", "c", "d", "e", "f" };
        Task[] tasks = new Task[IDs.Count];
        for (int i = 0; i < IDs.Count; i++)
            tasks[i] = await GetDataAsync(IDs[i]);
    
        Task.WaitAll(tasks);    
    }
    
    async static Task GetDataAsync(string id)
    {
        var data = await Task.Run(() => GetDataSynchronous(id));
        // some other logic to display data in ui
    }
    
    • John Koerner
      John Koerner about 9 years
      GetDataAsync is returning a task, however, Run is not. async void should only be used for event handlers.