155

We have this method:

async Task<int> AccessTheWebAsync()
{ 
    HttpClient client = new HttpClient();

   Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

   // You can do work here that doesn't rely on the string from GetStringAsync.
   DoIndependentWork();

   string urlContents = await getStringTask;
   //The thing is that this returns an int to a method that has a return type of Task<int>
   return urlContents.Length;
}

Does an implicit conversion occur between Task<int> and int? If not, then what is happening? How is it implemented to work?

2

2 Answers 2

234

Does an implicit conversion occur between Task<> and int?

Nope. This is just part of how async/await works.

Any method declared as async has to have a return type of:

  • void (avoid if possible)
  • Task (no result beyond notification of completion/failure)
  • Task<T> (for a logical result of type T in an async manner)

The compiler does all the appropriate wrapping. The point is that you're asynchronously returning urlContents.Length - you can't make the method just return int, as the actual method will return when it hits the first await expression which hasn't already completed. So instead, it returns a Task<int> which will complete when the async method itself completes.

Note that await does the opposite - it unwraps a Task<T> to a T value, which is how this line works:

string urlContents = await getStringTask;

... but of course it unwraps it asynchronously, whereas just using Result would block until the task had completed. (await can unwrap other types which implement the awaitable pattern, but Task<T> is the one you're likely to use most often.)

This dual wrapping/unwrapping is what allows async to be so composable. For example, I could write another async method which calls yours and doubles the result:

public async Task<int> AccessTheWebAndDoubleAsync()
{
    var task = AccessTheWebAsync();
    int result = await task;
    return result * 2;
}

(Or simply return await AccessTheWebAsync() * 2; of course.)

11
  • 5
    can any details be offered for how it works under the hood, just curious.
    – Freeman
    Oct 31, 2012 at 13:35
  • 13
    +1 Good answer as always. And why are you so pretty fast writing them?!
    – Felix K.
    Oct 31, 2012 at 13:36
  • 14
    +1: Just started looking into async/await and I find this extremely non-intuitive. IMO, there should be a keyword or similar at the return to make this clear, e.g. return async result; (in the same way that await result "unwraps" the T from the Tast<T>).
    – dav_i
    Nov 18, 2013 at 14:31
  • 2
    @JonSkeet But it doesn't make sense without the await - with T foo = someTaskT; you'd get "Cannot implicitly convert type Task<T> to T" - in the same way I argue that it would make more sense to have a keyword for the inverse (wrapping in Task<T>). I'm all for removing fluff but in this case I think it provides an unnecessary obfuscation within async methods. (Obviously the point is moot because the powers that be have already spoken/coded!)
    – dav_i
    Nov 18, 2013 at 14:55
  • 3
    @dav_i: The assignment doesn't make sense, but the rest does. And there are cases where the whole statement would make sense - although it might not be useful. Given that the method is already declared async, I think that's enough.
    – Jon Skeet
    Nov 18, 2013 at 14:58
18

No requires converting the Task to int. Simply Use The Task Result.

int taskResult = AccessTheWebAndDouble().Result;

public async Task<int> AccessTheWebAndDouble()
{
    int task = AccessTheWeb();
    return task;
}

It will return the value if available otherwise it return 0.

6
  • 30
    that is not what i asked.
    – Freeman
    Oct 25, 2016 at 9:14
  • 35
    This does not answer the question. But more importantly, this is very bad advice. You should almost never use Result; it can cause deadlocks! Consider for example this workflow: (1) Write a note that says "mow the lawn". (2) Wait for the lawn to be mowed (3) Eat a sandwich, (4) Do whatever it says on the note". With that workflow, you never eat a sandwich or mow the lawn, because step 2 is a synchronous wait on something that you will do in the future. But that's the workflow that you are describing here. Nov 19, 2018 at 20:33
  • 1
    @EricLippert: Not clear your example. Can you please explain how Result can introduce deadlocks when await won't?
    – CharithJ
    Nov 3, 2019 at 23:59
  • 4
    Await means to do something while you're waiting for the result and that something can include doing work to compute the result. But synchronous waits do nothing while you wait which means you could be preventing the work from being done. Nov 4, 2019 at 0:02
  • 1
    @EricLippert. Will this have the same issue ? 'Task.Run(()=> AccessTheWebAndDouble()).Result;'
    – CharithJ
    Nov 6, 2019 at 5:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.