await using 是 c# 8.0 引入的语法糖,专用于异步释放实现 iasyncdisposable 的资源;它调用 disposeasync()(返回 valuetask),而普通 using 调用同步 dispose(),二者不可互换,且 await using 仅限局部作用域使用。

什么是 await using,它和普通 using 有什么区别?
await using 是 C# 8.0 引入的语法糖,专用于异步释放实现了 IAsyncDisposable 的资源。它不替代 using,而是补充:
-
using调用IDisposable.Dispose()(同步) -
await using调用IAsyncDisposable.DisposeAsync()(异步,返回ValueTask)
常见误用是试图对只实现 IDisposable 的类型写 await using,编译器会直接报错:error CS8400: Feature 'async disposable' is not available in C# 7.3 或更具体的 cannot await ... because it does not implement 'IAsyncDisposable'。
必须确保:
- 目标类型显式实现 IAsyncDisposable(如 FileStream、HttpClient 在 .NET Core 3.0+ 中已实现)
- 项目语言版本 ≥ C# 8.0(在 .csproj 中确认 <langversion>8.0</langversion> 或更高)
- 目标框架为 .NET Core 3.0+ 或 .NET 5+(.NET Framework 不支持 IAsyncDisposable)
await using 的基本写法和作用域限制
await using 和 using 一样,仅在声明的作用域内有效,离开作用域时自动调用 DisposeAsync()。但它不能用于字段或类级别声明,只能在方法/局部作用域中使用。
正确示例:
public static async Task ReadFileAsync()
{
await using var stream = new FileStream("data.bin", FileMode.Open);
await using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
// stream 和 reader 的 DisposeAsync() 会在 } 处按逆序自动调用
}
错误写法(编译失败):
- 在
class中写await using FileStream _fs = ...;(不允许字段级await using) - 写成
await using (var x = ...) { ... }(C# 不支持这种括号形式,只支持声明式await using var x = ...;) - 混用同步与异步资源:如
await using var a = new MemoryStream();(MemoryStream没有实现IAsyncDisposable,会编译失败)
什么时候该用 await using,而不是手动 await x.DisposeAsync()?
核心判断依据是:是否需要确保异步清理 一定发生,且希望利用作用域自动管理(尤其在有异常、提前 return 或多层嵌套时)。
手动调用 await x.DisposeAsync() 容易遗漏,比如:
- 在
try块中提前return,忘了写finally - 多个资源需按顺序释放,手写容易出错
- 异步流处理中,资源生命周期与异步控制流耦合紧密(如数据库连接、HTTP 响应流)
而 await using 编译后会生成类似 try/finally 的结构,保证 DisposeAsync() 总被执行(即使抛出异常)。但注意:它不捕获异常 —— 如果 DisposeAsync() 抛出异常,且当前已有未处理异常,则新异常会被压制(和同步 Dispose() 行为一致)。
嵌套 await using 和性能影响要注意什么?
可以链式声明多个 await using,它们按声明逆序释放(和同步 using 一致),这对依赖关系很重要(例如 StreamReader 依赖 FileStream,必须先关 reader 再关 stream)。
示例:
await using var fs = new FileStream("log.txt", FileMode.Append);
await using var sw = new StreamWriter(fs); // 依赖 fs
await sw.WriteLineAsync("Done."); // 可能抛异常
// sw.DisposeAsync() 先执行,再 fs.DisposeAsync()
性能方面:DisposeAsync() 本身是否“昂贵”取决于实现。比如 FileStream.DisposeAsync() 主要刷缓冲区并关闭句柄,一般很快;但某些自定义资源可能触发网络调用或磁盘 flush,此时阻塞主线程的风险被移除,但整体耗时不变。别指望 await using 加速释放逻辑 —— 它只是让释放变成可等待、不阻塞线程的操作。
真正容易被忽略的是:异步释放不等于“后台静默释放”。它仍参与 async/await 状态机调度,若大量并发 await using 对象同时进入 DisposeAsync(),且其内部有 I/O,仍可能成为吞吐瓶颈。这时候得看具体实现,而不是语法本身。











