Task<int>如何成为int类型?

154
我们有这个方法:
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;
}

Task<int>int 之间会发生隐式转换吗?如果没有,那么是什么在发生?它是如何实现工作的?


1
请继续阅读:点击此处。我假设编译器会根据async关键字来处理这个问题。 - D Stanley
2
@Freeman,看看这个很好的解释:https://dev59.com/ym855IYBdhLWcg3w_5cQ#4047607 - qehgt
2个回答

228
隐式转换是否在Task<>和int之间发生?
不是的。这只是异步/等待工作方式的一部分。
任何声明为async的方法都必须具有以下返回类型:
void(尽可能避免)
Task(除了完成/失败通知外,没有其他结果)
Task(以异步方式返回类型为T的逻辑结果)
编译器会执行所有适当的包装。重点是您正在异步地返回urlContents.Length - 您无法使该方法仅返回int,因为实际方法将在第一个未完成的await表达式被触发时返回。因此,它返回一个Task,该任务将在异步方法本身完成时完成。
请注意,等待相反-它将Task解包为T值,这就是此行起作用的方式:
string urlContents = await getStringTask;

...但当然它会异步地解开它,而只使用Result会阻塞直到任务完成。(await可以解开实现可等待模式的其他类型,但Task<T>是你最常使用的类型)。

这种双重包装/解包装是使异步变得如此可组合的原因。例如,我可以编写另一个异步方法,调用您的方法并将结果加倍:

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

(或者简单地说,当然也可以返回 return await AccessTheWebAsync() * 2;。)

5
可以提供一些它如何在内部运作的细节吗?我只是好奇。 - Freeman
13
+1 像往常一样好的回答。你为什么写得这么快,而且还那么漂亮呢?! - Felix K.
14
刚开始学习 "async/await",但我觉得它非常不直观。我的看法是,在 return 处应该有一个类似的关键字,以使其清晰明了,例如 return async result;(就像 await resultTast<T> 中的 T“解开”一样)。 - dav_i
2
@JonSkeet 但是如果没有 await,它就没有意义 - 使用 T foo = someTaskT; 会得到“无法隐式转换类型 Task<T>T” - 同样的方式,我认为有一个关键字来反转(包装在 Task<T> 中)会更有意义。我完全赞成去除冗余,但在这种情况下,我认为它在 async 方法中提供了不必要的混淆。(显然,这一点已经无关紧要,因为权力已经发言/编码!) - dav_i
3
@dav_i说:“这个assignment没有意义,但其余部分都有。而且有些情况下整个语句会有意义,尽管可能没有用处。鉴于该方法已经声明为 async,我认为这已足够。” - Jon Skeet
显示剩余6条评论

17
不需要将任务转换为整数。只需使用任务结果即可。
int taskResult = AccessTheWebAndDouble().Result;

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

如果有值,它将返回该值;否则返回0。


30
这不是我所问的。 - Freeman
35
这并没有回答问题,但更重要的是,这是非常糟糕的建议。你几乎永远不应该使用 Result,它会导致死锁!例如考虑以下工作流程:(1)写一个便签说“修剪草坪”。(2)等待草坪被修剪,(3)吃一个三明治,(4)做便签上说的事情。在这个工作流程中,你永远不会吃三明治或修剪草坪,因为步骤2是对将来要完成的某些事情进行同步等待。但这正是你在这里描述的工作流程。 - Eric Lippert
1
@EricLippert:你的例子不太清楚。能否请您解释一下,为什么Result会引起死锁而await不会? - CharithJ
4
Await 的意思是在等待结果的同时做某些事情,而这些事情可以包括计算结果。但同步等待在等待期间什么也不做,这意味着你可能会阻止工作的进行。 - Eric Lippert
1
@EricLippert。这个会有同样的问题吗?'Task.Run(()=> AccessTheWebAndDouble()).Result;' - CharithJ
1
".Result 可能会阻塞,通常被认为是一个非常糟糕的想法。我无法相信这个有任何赞同,更不用说 22 个了。" - db2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接