金庆的专栏

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  423 随笔 :: 0 文章 :: 454 评论 :: 0 Trackbacks
# C#异步方法返回void和Task的区别

(金庆的专栏 2021.2)

如果异步(async关键字)方法有返回值,返回类型为T时,返回类型必然是 `Task<T>`。
但是如果没有返回值,异步方法的返回类型有2种,一个是返回 Task, 一个是返回 void:
```
    public async Task CountDownAsync(int count)
    {
        for (int i = count; i >= 0; i--)
        {
            await Task.Delay(1000);
        }
    }

    public async void CountDown(int count)
    {
        for (int i = count; i >= 0; i--)
        {
            await Task.Delay(1000);
        }
    }
```

调用时,如果返回 Task, 但返回值被忽略时,VS 会用绿色波浪线警告:
```
    CountDownAsync(3);
    ~~~~~~~~~~~~~~~~~
```

信息为:
```
(awaitable) Task AsyncExample.CountDownAsync(int count)

Usage:
 await CountDownAsync(...);

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
```

中文为:
```
CS4014:由于此调用不会等待,因此在此调用完成之前将会继续执行当前方法。请考虑将"await"运算符应用于调用结果。
```

添加 await 后就正常了:
```
    await CountDownAsync(3);
```

如果调用者不是一个异步方法,因为只有在异步方法中才可以使用 await,
或者并不想在此等待,如想同时执行多个 CountDownAsync(),
就不能应用 await 来消除警告。

此时可以改用 void 返回值的版本:
```
void Test()
{
    ...
    CountDown(3);
    CountDown(3);
    ...
}

async void CountDown(int count)
{
    for (int i = count; i >= 0; i--)
    {
        await Task.Delay(1000);
    }
}
```

> Never call `async Task` methods without also awaiting on the returned Task. If you don’t want to wait for the async behaviour to complete, you should call an `async void` method instead.

摘自:http://www.stevevermeulen.com/index.php/2017/09/using-async-await-in-unity3d-2017/

CountDown() 可以直接调用 CountDownAsync() 实现:
```
async void CountDown(int count)
{
    await CountDownAsync(count);
}
```

使用下划线变量忽略异步方法的返回值也可以消除警告:
```
void Test()
{
    ...
    _ = CountDownAsync(3);
    _ = CountDownAsync(3);
    ...
}
```

但是这样同时也会忽略 CountDownAsync() 中的异常。如以下异常会被忽略。

```
void Test()
{
    ...
    _ = CountDownAsync(3);
    ...
}

async Task CountDownAsync(int count)
{
    for (int i = count; i >= 0; i--)
    {
        await Task.Delay(1000);
    }
    throw new Exception();
}
```

如果是调用返回 void 的异步方法,Unity 会报错:
```
Exception: Exception of type 'System.Exception' was thrown.
```

## 对 Async 后缀的说明

```
You could say that the Async suffix convention is to communicate to the API user that the method is awaitable. For a method to be awaitable, it must return Task for a void, or Task<T> for a value-returning method, which means only the latter can be suffixed with Async.
```

摘自:https://stackoverflow.com/questions/15951774

grpc 生成的代码中,异步请求返回了一个 AsyncCall 对象,AsyncCall 实现了 GetAwaiter() 接口:
```
      public virtual grpc::AsyncUnaryCall<global::Routeguide.Feature> GetFeatureAsync(global::Routeguide.Point request, ...)
```

可以这样调用并等待:
```
    var resp = await client.GetFeatureAsync(req);
```

虽然返回类型不是`Task<>`, 但是可等待,所以添加了 Async 后缀。
posted on 2021-02-25 10:38 金庆 阅读(1069) 评论(0)  编辑 收藏 引用 所属分类: 9. 其它

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理