68
votes

I am trying to master async method syntax in .NET 4.5. I thought I had understood the examples exactly however no matter what the type of the async method is (ie Task<T>), I always get the same type of error error on the conversion back to T - which I understood was pretty much automatic. The following code produces the error:

Cannot implicitly convert type 'System.Threading.Tasks.Task<System.Collections.Generic.List<int>>' to 'System.Collections.Generic.List<int>'

public List<int> TestGetMethod()
{
    return GetIdList(); // compiler error on this line
}


async Task<List<int>> GetIdList()
{
    using (HttpClient proxy = new HttpClient())
    {
        string response = await proxy.GetStringAsync("www.test.com");
        List<int> idList = JsonConvert.DeserializeObject<List<int>>();
        return idList;
    }
}

It fails if I explicitly cast the result as well. This:

public List<int> TestGetMethod()
{
    return (List<int>)GetIdList();  // compiler error on this line
}

somewhat predictably results in this error:

Cannot convert type 'System.Threading.Tasks.Task<System.Collections.Generic.List<int>>' to 'System.Collections.Generic.List<int>'

Any help greatly appreciated.

3
The 'automatic unwrapping' of Task<T> to T is the effect of await'ing the task. If you change TestGetMethod to be async then it could await GetIdList() to get the T into a local var - James Manning
Unrelated, but convention would recommend GetIdListAsync() although not a big deal for internal use, of course. :) - James Manning
Thanks for your replies James. I had previously tried that but if I change GetTestMethod() to use await, then it must also be async so then I would need to change it's type to Task<List<int>> as well. And then it's caller will have the same issue as GetTestMethod() does now? - PaulR
OK I think I understand now - if I make GetTestMethod() void, then this does not happen. But as long as a method has a return type, it's caller must also be async and use await in the call etc. - PaulR
When you call something that returns a Task, you don't have to use await. It really depends on what the caller is supposed to do with it. Fire and forget? Schedule a continuation on it? Block until it completes? Something else? For a test method, assuming you can use a test framework that supports async test methods, I'd use await in an async test method, but there are other options. - James Manning

3 Answers

98
votes

The main issue with your example that you can't implicitly convert Task<T> return types to the base T type. You need to use the Task.Result property. Note that Task.Result will block async code, and should be used carefully.

Try this instead:

public List<int> TestGetMethod()  
{  
    return GetIdList().Result;  
}
34
votes

You need to make TestGetMethod async too and attach await in front of GetIdList(); will unwrap the task to List<int>, So if your helper function is returning Task make sure you have await as you are calling the function async too.

public Task<List<int>> TestGetMethod()
{
    return GetIdList();
}    

async Task<List<int>> GetIdList()
{
    using (HttpClient proxy = new HttpClient())
    {
        string response = await proxy.GetStringAsync("www.test.com");
        List<int> idList = JsonConvert.DeserializeObject<List<int>>();
        return idList;
    }
}

Another option

public async void TestGetMethod(List<int> results)
{
    results = await GetIdList(); // await will unwrap the List<int>
}
3
votes

Depending on what you're trying to do, you can either block with GetIdList().Result ( generally a bad idea, but it's hard to tell the context) or use a test framework that supports async test methods and have the test method do var results = await GetIdList();