C# 中异步方法返回类型概述
介绍
在 C# 中使用async/await时,您可能会遇到一些编译器警告和错误,尤其是与返回类型相关的警告和错误。事实证明,标记为async的方法的调用者的要求因方法的返回类型而异。更重要的是,更改异步方法的返回类型可能会具有传染性,在某些情况下,您不仅需要更改其调用者的签名,还需要更改其调用者的调用者的签名等等。虽然这最初可能被视为一种烦恼,但它实际上是一个重要的主题,因为返回类型会影响代码的执行顺序。
异步方法返回类型一览
C#中标有async 的方法必须返回下列之一:
- 任务
- 任务<T>
- 价值任务
- 值任务<T>
- 空白
这不是一份完整的列表。虽然如何执行此操作的详细信息超出了本指南的范围,但值得注意的是,从 C# 7.0 开始,您还可以定义自己的类似任务的类型。不过,这是一个不常见的要求。
要清楚的是,当我们说返回类型时,我们指的是方法签名中方法名称之前的内容。例如:
public static async Task MyMethod(int myParameter)
{
...
}
在上述方法签名中,public、static和async都被称为“修饰符”。C# 编译器不会强制执行这些修饰符的顺序,但通常,作为惯例,async修饰符放在最后,就在返回类型(本例中为Task)之前。正是由于这种典型的定位,C# 中的异步方法通常被称为async void或async Task。这两者之间的对比很重要,我们接下来会看到。
为什么你应该坚持完成任务
如果您对上面的返回类型列表感到不知所措,请让我让您放心。大多数情况下,您应该只使用具有异步方法的返回类型Task或Task<T>。上面列表中的所有其他内容都适用于不太常见的特定情况。
无论如何,您可以选择返回Task或返回void。但即使是这种选择也是不平衡的,严重偏向于使用Task。为什么?如果方法返回void,则不允许该方法的调用者等待它。如果您不等待方法,则调用者的执行可能会在方法完成之前继续。更成问题的是,当调用者不等待异步方法时,它无法正确处理异常。从异步方法返回void有几个有效的原因,但绝大多数时候您应该返回Task以便您可以等待它。
何时使用任务,何时使用任务
如果在Task和void之间进行选择很容易,那么在Task和Task<T>之间进行选择就更容易了。当您需要从方法中返回某些信息时,请使用Task<T> ,当不需要时,请使用Task 。
Task <T>类具有类型为T的Result属性,其中包含您在方法中使用return语句传回的任何内容。调用者通常通过使用await隐式检索存储在Result属性中的内容。例如,以下GetHtml方法将返回从远程服务器检索到的 html 字符串,如果未指定url ,则返回 null 。
async Task<string> GetHtml(string url)
{
if (string.IsNullOrEmpty(url))
{
return null;
}
string html = await new HttpClient().GetStringAsync(url);
return html;
}
可以通过明确引用 Result 属性来访问上述方法返回的 html:
Task<string> myTask = client.GetHtml("http://...");
await myTask;
string html = myTask.Result;
但通常情况下,通过await隐式地内联访问它会更容易、更易读:
string html = await GetHtml("http://...");
需要明确的是,使用普通的Task时,不能从异步方法返回任何内容。例如,这将无法编译:
async Task GetHtml(string url)
{
return await new HttpClient().GetStringAsync(url);
}
...但是这会:
async Task<string> GetHtml(string url)
{
return await new HttpClient().GetStringAsync(url);
}
另一种思考方式是,在异步方法中选择Task和Task<T>类似于在同步方法中选择void和任何其他类型。例如,以下同步方法:
void DoOperation()
{
SomeOperation();
}
byte[] GetData()
{
return GetData("/path/to/data");
}
...当与Task.Run “异步” 时,将变成:
async Task DoOperation()
{
await Task.Run(SomeOperation);
}
async Task<byte[]> GetData()
{
return await Task.Run(() => GetData("/path/to/data"));
}
异步方法中允许的参数类型
需要将Task<T>传回数据给调用者的原因之一是异步方法不允许有ref或out参数。
C# 7.2 中引入的in参数修饰符在异步方法中也是不允许的。
例如,以下内容将导致编译器错误:
async Task<bool> TryGetHtml(string url, out string html)
在这种情况下,您无法使用ref或out参数返回数据。实际上,从异步方法返回数据的唯一方法是使用Task<T>。但好处是T可以是任何东西。它可以是值类型,例如int或bool,也可以是任何引用类型,包括集合、数组或您自己的自定义类。如果您发现自己想要从异步方法返回多个变量,则可以定义一个包含所需所有内容的类并返回该类的实例,或者,如果这不方便,您可以返回 Tuple <T1, T2> 。作为示例,我们可以使用TryGetHtml实现我们之前尝试的操作,如下所示:
static async Task<(bool, string)> TryGetHtml(string url)
{
if (string.IsNullOrEmpty(url))
{
return (false, null);
}
string html = await new HttpClient().GetStringAsync(url);
return (true, html);
}
我们可以使用以下方法调用此方法:
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~