当前位置: 首页 > 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 代码(用于模拟用户输入等)
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • HTTP 简介
  • Iterator 和 for...of 循环
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • JS专题之继承
  • nodejs调试方法
  • opencv python Meanshift 和 Camshift
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • webpack入门学习手记(二)
  • 基于web的全景—— Pannellum小试
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 驱动程序原理
  • 通过npm或yarn自动生成vue组件
  • 一、python与pycharm的安装
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • # 计算机视觉入门
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • #预处理和函数的对比以及条件编译
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (动态规划)5. 最长回文子串 java解决
  • (离散数学)逻辑连接词
  • (十六)串口UART
  • (转)原始图像数据和PDF中的图像数据
  • .Net 6.0 处理跨域的方式
  • .Net Core和.Net Standard直观理解
  • .net MySql
  • .Net Winform开发笔记(一)
  • .net 验证控件和javaScript的冲突问题
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • @Autowired @Resource @Qualifier的区别
  • @data注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • [ vulhub漏洞复现篇 ] struts2远程代码执行漏洞 S2-005 (CVE-2010-1870)
  • [《百万宝贝》观后]To be or not to be?
  • [20171102]视图v$session中process字段含义
  • [AIGC] Java 和 Kotlin 的区别