72

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.

svick
  • 236,525
  • 50
  • 385
  • 514
PaulR
  • 861
  • 1
  • 8
  • 11
  • 4
    The 'automatic unwrapping' of Task 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 Oct 14 '12 at 21:47
  • 1
    Unrelated, but convention would recommend GetIdListAsync() although not a big deal for internal use, of course. :) – James Manning Oct 14 '12 at 21:48
  • 1
    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> as well. And then it's caller will have the same issue as GetTestMethod() does now? – PaulR Oct 14 '12 at 22:01
  • 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 Oct 14 '12 at 22:22
  • @PaulR but if you make an async void method, you have no way of guaranteeing that it completes execution; the process could exit first. – phoog Oct 14 '12 at 23:13
  • 1
    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 Oct 15 '12 at 01:31
  • Thanks everyone for your help - you have enabled me to understand this better with your posts. I think I misunderstood where an async method could be called from - I wanted to bury the call in a library of helper functions somewhere. But - in my app anyway - I think it needs to be called from an event handler to allow the system to utilise the wait time. – PaulR Oct 15 '12 at 19:41

5 Answers5

105

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;  
}
user2388853
  • 1,485
  • 1
  • 11
  • 4
  • 17
    [`Result` could easily cause deadlocks](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). – Stephen Cleary Jul 13 '17 at 13:40
  • 1
    The proper way to unwrap a `Task` is to put `await` in front of it. Official docs: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/ – Asker Dec 15 '22 at 05:48
35

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>
}
Sergey Litvinov
  • 7,408
  • 5
  • 46
  • 67
Mayank
  • 8,777
  • 4
  • 35
  • 60
  • Note that because there's nothing else going on in this TestGetMethod, doing the unwrap+rewrap is a tad silly and it could remove the async and await and just return GetIdList() :) – James Manning Oct 15 '12 at 01:33
  • 1
    what is the need that `TestGetMethod` has to be `async`? – techBeginner Feb 02 '13 at 01:25
3

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();

James Manning
  • 13,429
  • 2
  • 40
  • 64
1

I've just had this issue. I was trying to return a value, but it could not be implicitly cast to a type of Task. The solution was to add the missing async keyword that I forgot to add.

Jason Alls
  • 199
  • 1
  • 7
0

I just had the same issue pop up but the resolution was different than the others. I'm working with two async calls within an async method, this code is what was giving me the error:

var fileContents = reader.ReadToEndAsync();
if (fileContents != null)
{
     var profile = await Task.Run(() => JsonConvert.DeserializeObject<T>(fileContents));
     return profile;
}

This is the fix. I had forgotten the async on the first line:

var fileContents = await reader.ReadToEndAsync();
if (fileContents != null)
{
     var profile = await Task.Run(() => JsonConvert.DeserializeObject<T>(fileContents));
     return profile;
}

The error message was showing on "fileContents" on the line var profile = ... So it wasn't immediately obvious of where the error was.

Zonus
  • 2,313
  • 2
  • 26
  • 48