IDisposable 与 using一、IDisposable显式释放资源的契约1. 为什么要使用IDisposable先看一个问题C# 会自动清理垃圾为什么还需要手动释放C# 的内存比如你 new 出来的对象确实由垃圾回收器GC自动管理你不用管。但是程序里还有一些不属于内存的东西比如文件你打开了一个文件就占用了操作系统的文件句柄数据库连接你连上了数据库数据库服务器那边有资源网络端口你发 HTTP 请求系统开了端口绘图画笔你画图时用的 GDI 对象这些东西GC 不认识也不会帮你去关。如果你不主动释放它们会一直存在导致文件被你的程序一直占着别人打不开数据库连接数爆满新用户连不上程序越跑越慢最后崩溃所以C# 给了你一个约定如果你用了这种特殊资源你用完必须亲自归还。这个约定就是IDisposable。2. IDisposable 就像一张“归还资源的标签”C# 源码里面的IDisposable 是一个接口里面只有一句话voidDispose();任何类比如 FileStream、SqlConnection只要实现了这个接口就等于贴了个标签“我占用了特殊资源用完请调用我的 Dispose() 方法归还”。FileStreamfsnewFileStream(a.txt,FileMode.Open);// ... 使用文件fs.Dispose();// 归还文件但是这样写有个隐患如果中间代码抛出异常Dispose() 可能永远不会执行资源照样泄露。你还得写 try-finallyFileStreamfsnull;try{fsnewFileStream(a.txt,FileMode.Open);// 使用文件}finally{if(fs!null)fs.Dispose();}太麻烦而且容易忘。于是 C# 提供了一个语法糖using 。3. 什么时候自己实现 IDisposable如果你的类里面包含了需要释放的东西那么你的类也应该实现 IDisposable。class Logger : IDisposable{ private FileStream logFile; // 这个需要释放 public Logger(string path){ logFile new FileStream(path, FileMode.Append); } public void Write(string msg) { // 写日志 } public void Dispose(){ logFile?.Dispose(); // 释放掉内部的资源 } }然后别人用你的 Logger 时using(varloggernewLogger(app.log)){logger.Write(程序启动);}// 自动释放文件原则如果你的类里有任何 IDisposable 类型的成员比如 FileStream、SqlConnection你就应该实现 IDisposable并在 Dispose() 里调用它们的 Dispose()。二、using 语句优雅且安全的自动释放1. using 就是帮你自动写 try-finally 的偷懒工具using 语句的作用出了这个大括号自动调用 Dispose()不管有没有异常。基本用法using(FileStreamfsnewFileStream(a.txt,FileMode.Open)){// 使用 fs 读文件}// 到这里自动 fs.Dispose()-----------------------------------------//而上面的using基本用法和下面这段代码实现是一样的FileStreamfsnewFileStream(a.txt,FileMode.Open);try{// 使用 fs}finally{if(fs!null)fs.Dispose();}所以 using 的本质 自动 安全的资源归还2. 如果用 using 包多个资源等价于嵌套的 try-finally保证两个都被释放。using(FileStreaminputFile.OpenRead(1.txt))using(FileStreamoutputFile.OpenWrite(2.txt)){// 复制文件内容}// 先自动释放 output再释放 input3. C# 8 之后更简单的写法using 声明usingvarfsnewFileStream(a.txt,FileMode.Open);// 使用 fs// 方法结束时会自动 Dispose()效果fs 的生命周期持续到方法结束而不是大括号结束。适合那些“在整个方法里都要用最后一起释放”的场景。voidReadFile(){usingvarfsnewFileStream(a.txt,FileMode.Open);// 在这里使用 fs// 方法是这里结束fs 自动释放}4. 延迟释放与手动控制生命周期有些场景你不想在代码块结束立刻释放比如把对象返回给调用方使用。这时就不能在方法内 using。而在创建资源的责任交给调用方调用方负责 using。// 不负责释放publicStreamGetStream(){returnnewFileStream(data.bin,FileMode.Open);}// 调用方using(varstreamGetStream()){// 调用方负责释放}四、Dispose 模式如果你写一个类内部只包含托管资源例如另一个 IDisposable 对象只需要publicclassMyClass:IDisposable{privateSqlConnectionconn;publicvoidDispose()conn?.Dispose();}这完全正确对于大多数日常业务代码已经足够。但是当你面对以下情况时简单的 Dispose 就不够了类直接持有非托管资源比如通过 IntPtr 获得的文件句柄、GDI 对象、原生内存类可能被继承派生类也可能有自己的资源需要释放用户可能忘记调用 Dispose导致非托管资源泄漏为了同时解决这三个问题.NET 设计了一个经典的 Dispose 模式也叫“可释放模式”它包含Dispose() 公共方法protected virtual void Dispose(bool disposing) 方法终结器析构函数GC.SuppressFinalize(this)Dispose 模式 其实在实际开发并不常见这里详情就懒得写了Dispose 模式 一个 Dispose() 方法 一个受保护的 Dispose(bool) 虚方法 一个终结器仅当有非托管资源时。本文参考阿里云开发者社区 https://developer.aliyun.com/article/805187C# 官方文档 https://learn.microsoft.com/zh-cn/dotnet/standard/garbage-collection/using-objects博客园 https://www.cnblogs.com/chenlight/p/16558148.html