当前位置: 首页 > news >正文

.NET 中什么样的类是可使用 await 异步等待的?

我们已经知道 Task 是可等待的,但是去看看 Task 类的实现,几乎找不到哪个基类、接口或者方法属性能够告诉我们与 await 相关。

而本文将探索什么样的类是可使用 await 异步等待的?


Dixin’s Blog - Understanding C# async / await (2) The Awaitable-Awaiter Pattern 一文解决了我们的疑惑。async/await 是给编译器用的,只要我们的类包含一个 GetAwaiter 方法,并返回合适的对象,我们就能让这个类的实例被 await 使用了。

既然需要一个 GetAwaiter 方法,那我们先随便写个方法探索一下:

Test DoAsync()
{
    return new Test();
}
class Test
{
    void GetAwaiter()
    {
    }
}

尝试调用:

await DoAsync();

编译器告诉我们:

Test.GetAwaiter() 不可访问,因为它具有一定的保护级别。

原来 GetAwaiter 方法需要是可以被调用方访问到的才行。

于是我们将 GetAwaiter 前面的访问修饰符改成 public。现在提示变成了:

await 要求类型 Test 包含适当的 GetAwaiter 方法。

考虑到一定要获取到某个对象才可能有用,于是我们返回一个 Test2 对象:

public class Test
{
    public Test2 GetAwaiter()
    {
        return new Test2();
    }
}

public class Test2
{
}

这时编译器又告诉我们:

Test2 未包含 IsCompleted 的定义。

加上 public bool IsCompleted { get; },编译器又说:

Test2 不实现 INotifyCompletion。

于是我们实现之,编译器又告诉我们:

Test2 未包含 GetResult 的定义。

于是我们加上一个空的 GetResult 方法,现在编译器终于不报错了。

现在我们一开始的 DoAsync 和辅助类型变成了这样:

// 注:此处为试验代码。
private Test DoAsync()
{
    return new Test();
}

public class Test
{
    public Test2 GetAwaiter()
    {
        return new Test2();
    }
}

public class Test2 : INotifyCompletion
{
    public bool IsCompleted { get; }
    public void GetResult() { }
    public void OnCompleted(Action continuation) { }
}

总结起来,要想使一个方法可被 await 等待,必须具备以下条件:

  1. 这个方法返回一个类 A 的实例,这个类 A 必须满足后面的条件。
  2. 此类 A 有一个可被访问到的 GetAwaiter 方法(扩展方法也行,这算是黑科技吗?),方法返回类 B 的实例,这个类 B 必须满足后面的条件;
  3. 此类 B 实现 INotifyCompletion 接口,且拥有 bool IsCompleted { get; } 属性、GetResult() 方法、void OnCompleted(Action continuation) 方法。

更多编写自定义 Awaiter 的文章可以阅读:

入门篇:

  • .NET 中什么样的类是可使用 await 异步等待的?
  • 定义一组抽象的 Awaiter 的实现接口,你下次写自己的 await 可等待对象时将更加方便
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?

实战篇:

  • 在 WPF/UWP 中实现一个可以用 await 异步等待 UI 交互操作的 Awaiter
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter

参考资料

  • Dixin’s Blog - Understanding C# async / await (2) The Awaitable-Awaiter Pattern

相关文章:

  • Visual Studio 2017 以前的旧格式的 csproj Import 进来的 targets 文件有时不能正确计算属性(PropertyGroup)和集合(ItemGroup)
  • 使用 ReSharper,输入即遵循 StyleCop 的代码格式化规范
  • StyleCop 是什么,可以帮助团队带来什么价值?
  • 文件和文件夹不存在的时候,FileSystemWatcher 监听不到文件的改变?如果递归地监听就可以了
  • C#/.NET 使用 CommandLineParser 来标准化地解析命令行
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • 使用 WPF 开发一个 Windows 屏幕保护程序
  • 在 Windows 10 中开启移动 WLAN 热点
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • 在 Roslyn 分析语法树时添加条件编译符号的支持
  • 自然码的形码
  • 出于迁移项目的考虑,GitHub 中 Fork 出来的项目,如何与原项目断开 Fork 关系?
  • 只需 5 秒钟,你就能取到 WPF 程序的超高分辨率超高清截图
  • 谨慎使用 FileInfo.Exists 实例方法,而是使用 File.Exists 静态方法替代
  • UWP 在 WebView 中执行 JavaScript 代码(用于模拟用户输入等)
  • CSS相对定位
  • css属性的继承、初识值、计算值、当前值、应用值
  • k个最大的数及变种小结
  • node 版本过低
  • vue:响应原理
  • 多线程 start 和 run 方法到底有什么区别?
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • No resource identifier found for attribute,RxJava之zip操作符
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • #考研#计算机文化知识1(局域网及网络互联)
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (二)fiber的基本认识
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (剑指Offer)面试题34:丑数
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET 反射的使用
  • .NET 中使用 Mutex 进行跨越进程边界的同步
  • []我的函数库
  • [《百万宝贝》观后]To be or not to be?
  • [2021ICPC济南 L] Strange Series (Bell 数 多项式exp)
  • [2669]2-2 Time类的定义
  • [Angular] 笔记 20:NgContent
  • [Asp.net MVC]Bundle合并,压缩js、css文件
  • [AX]AX2012 AIF(四):文档服务应用实例
  • [BPU部署教程] 教你搞定YOLOV5部署 (版本: 6.2)
  • [BUAA软工]第一次博客作业---阅读《构建之法》
  • [bzoj 3124][sdoi 2013 省选] 直径
  • [bzoj 3534][Sdoi2014] 重建
  • [Electron] 将应用打包成供Ubuntu、Debian平台下安装的deb包
  • [ffmpeg] 定制滤波器