无需Task异步返回类型的前提条件及自定义异步类型实现咨询
嘿,我懂你想实现一个能支持async/await语法的自定义类型——准确来说这是可等待模式(Awaitable Pattern),不是“异步模式”,先帮你纠正下术语~
要让一个类型能被await,核心要满足两个规则:
- 这个类型(可等待类型)必须有
GetAwaiter()方法,返回一个**等待器(Awaiter)**类型 - 等待器类型必须符合:
- 实现
INotifyCompletion(或更底层的ICriticalNotifyCompletion)接口 - 拥有
bool IsCompleted只读属性 - 拥有
GetResult()方法(返回结果或void)
- 实现
先看你现有代码里的几个关键问题:
GetResult()不能加async!这个方法是用来同步获取已完成异步操作的结果的,加async会让它返回Task<MyAsync>,完全不符合等待器的要求GetAwaiter()每次返回新的MyAsync实例会导致状态丢失,应该返回和当前异步操作关联的等待器(可以是当前实例,也可以是内部封装的等待器对象)OnCompleted直接抛异常等于没实现延续逻辑,这个方法的核心是在异步操作完成后触发后续代码(也就是await之后的逻辑)
下面给你一个可运行的修正示例,先做最简单的“自包含”实现(可等待类型同时作为等待器):
using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; // 同时作为可等待类型和等待器 class MyAsync : INotifyCompletion { // 标记异步操作是否完成 public bool IsCompleted { get; private set; } // 存储异步操作结果(如果不需要返回结果可以去掉) private int _operationResult; // 可等待类型必须的方法:返回自身作为等待器 public MyAsync GetAwaiter() { return this; } // 同步获取结果:不能加async! public int GetResult() { if (!IsCompleted) throw new InvalidOperationException("异步操作还没完成哦"); return _operationResult; } // 实现INotifyCompletion:注册延续逻辑 public void OnCompleted(Action continuation) { // 模拟一个异步操作(比如耗时IO、计算) Task.Run(async () => { await Task.Delay(1000); // 模拟1秒耗时操作 _operationResult = 42; // 设置操作结果 IsCompleted = true; // 标记操作完成 continuation(); // 触发await之后的代码 }); } // 可选:提供创建实例的入口 public static MyAsync StartAsyncOperation() { return new MyAsync(); } }
然后你就可以像这样使用它:
public async Task TestMyAsync() { int result = await MyAsync.StartAsyncOperation(); Console.WriteLine($"异步操作结果:{result}"); }
如果你的场景需要更清晰的封装(比如不想把可等待类型和等待器混在一起),可以拆分出内部等待器类,还能复用Task的底层逻辑:
class MyAsync { private readonly Task<int> _innerTask; private MyAsync(Task<int> innerTask) { _innerTask = innerTask; } // 返回自定义等待器 public MyAsyncAwaiter GetAwaiter() { return new MyAsyncAwaiter(_innerTask); } public static MyAsync Create() { return new MyAsync(Task.Run(async () => { await Task.Delay(1000); return 42; })); } // 内部等待器类,封装异步状态 public class MyAsyncAwaiter : INotifyCompletion { private readonly Task<int> _task; public MyAsyncAwaiter(Task<int> task) { _task = task; } public bool IsCompleted => _task.IsCompleted; public int GetResult() { // 复用Task等待器的结果获取逻辑 return _task.GetAwaiter().GetResult(); } public void OnCompleted(Action continuation) { // 复用Task的延续逻辑 _task.GetAwaiter().OnCompleted(continuation); } } }
最后再划几个重点:
- 等待器的
GetResult必须是同步方法,绝对不能加async OnCompleted一定要正确触发传入的continuation,不然await之后的代码永远不会执行- 如果没有特殊需求(比如自定义异步资源、非Task的异步操作),直接用
Task/Task<T>会省很多事,自定义可等待类型一般是针对特殊场景的
内容的提问来源于stack exchange,提问作者Bercovici Adrian
相关产品推荐
相关产品推荐

