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

C# 析构函数全详解:原理+实战+避坑

很多学过 C++ 的同学,初学 C# 都会踩一个大坑:试图用 C++ 的析构思维写 C# 代码

在 C++ 中,析构函数是确定性执行的,对象销毁立刻触发;但在 C# 中,析构函数完全是另一套逻辑,乱用会导致内存泄漏、程序卡顿、资源残留等问题。

今天这篇博文,一次性讲透C# 析构函数:是什么、什么时候执行、怎么写、怎么用、以及90%人都会犯的错误,附带全套可运行代码。


一、什么是 C# 析构函数?

1. 核心定义

析构函数(终结器)是类的特殊方法,由CLR 垃圾回收器(GC)自动调用,作用是:兜底释放非托管资源

简单记住两句话:

  • 托管资源 GC 自动管,不用你操心

  • 非托管资源 GC 管不了,靠析构函数兜底

2. 标准语法规则

语法特征极强,一眼就能识别:

  • ~波浪号开头 + 类名

  • 无返回值、无参数、无访问修饰符(不能加 public/private)

  • 一个类只能有一个析构函数,不支持重载

3. 最简入门代码示例

using System; namespace DestructorDemo { public class Student { // 构造函数:创建对象时执行 public Student() { Console.WriteLine("对象创建:构造函数执行"); } // 析构函数:GC回收对象前自动执行 ~Student() { Console.WriteLine("对象销毁:析构函数执行"); } } class Program { static void Main(string[] args) { // 创建对象 Student stu = new Student(); // 主动置空,让GC可以回收 stu = null; // 手动触发垃圾回收(仅测试用!项目中禁止写) GC.Collect(); GC.WaitForPendingFinalizers(); } } }
运行结果:

对象创建:构造函数执行

对象销毁:析构函数执行

💡 注意:日常开发绝对不要手动写 GC.Collect()!此处仅为测试,让大家直观看到析构函数效果。


二、析构函数 VS 构造函数(图文对比逻辑)

为了方便大家理解,我用最直白的生命周期逻辑展示:

生命周期流程图解

【代码执行 new】→ 构造函数(初始化资源) ↓ 【对象使用中】→ 执行业务逻辑 ↓ 【对象无引用、闲置】→ GC扫描标记 ↓ 【垃圾回收】→ 析构函数(清理资源) ↓ 【对象彻底销毁】

核心区别对照表

对比维度

构造函数

析构函数

执行时机

new 对象时,确定性执行

GC回收时,不确定时机

调用者

开发者代码主动调用

CLR 垃圾回收器自动调用

作用

初始化字段、开辟资源

兜底释放非托管资源

数量

可多个、可重载

只能1个、不可重载

手动调用

支持

完全不支持


三、重中之重:托管资源 & 非托管资源

能不能用好析构函数,核心就在于分清这两种资源!

1. 托管资源(GC 自动回收)

由 .NET 框架管理的资源,GC 会自动清理,不需要析构函数

例:string、List、数组、自定义类对象 等

2. 非托管资源(GC 管不了,必须兜底)

操作系统原生资源,.NET 无法自动识别回收,必须手动释放/析构兜底

例:文件流、数据库连接、网络套接字、IntPtr 非托管内存、窗口句柄

核心结论: 没有非托管资源的类,坚决不写析构函数,只会增加 GC 性能开销!


四、企业级标准写法:Dispose 释放模式

纯析构函数有致命问题:执行时机太晚、不确定。 所以 .NET 官方规范:IDisposable + 析构函数 双保险模式

  • 用户主动 using/Dispose:立刻释放所有资源(高效、及时)

  • 用户忘记释放:析构函数兜底,防止内存泄漏

完整可运行实战代码

using System; namespace DestructorDemo { // 实现释放接口 public class FileResource : IDisposable { private IntPtr _unmanagedHandle; // 模拟非托管资源句柄 private bool _isDisposed = false; // 构造函数:占用非托管资源 public FileResource() { Console.WriteLine("获取非托管资源句柄"); _unmanagedHandle = (IntPtr)10086; // 模拟资源赋值 } // 公共释放方法:给开发者手动调用 public void Dispose() { Dispose(true); // 告诉GC:资源已释放,无需执行析构函数 GC.SuppressFinalize(this); Console.WriteLine("手动释放资源完成,跳过析构执行"); } // 核心释放逻辑 protected virtual void Dispose(bool isManualDispose) { if (_isDisposed) return; // 主动释放时:可清理托管资源 if (isManualDispose) { Console.WriteLine("清理托管资源"); } // 无论手动/GC回收:必须清理非托管资源 if (_unmanagedHandle != IntPtr.Zero) { Console.WriteLine("释放非托管资源句柄"); _unmanagedHandle = IntPtr.Zero; } _isDisposed = true; } // 析构函数:GC兜底执行 ~FileResource() { Dispose(false); Console.WriteLine("析构函数兜底释放完成"); } } class Program { static void Main(string[] args) { // using 语法糖:出作用域自动调用Dispose using (var file = new FileResource()) { Console.WriteLine("正在使用资源..."); } Console.WriteLine("程序执行结束"); } } }

运行结果

获取非托管资源句柄 正在使用资源... 清理托管资源 释放非托管资源句柄 手动释放资源完成,跳过析构执行 程序执行结束

可以看到:手动 Dispose 成功后,析构函数不会执行,性能最优!


五、析构函数 4 大高频误区(避坑必看)

误区1:C# 析构 = C++ 析构

错误:C++ 析构确定性执行,C# 析构完全随机,程序退出都可能不执行!

正确:需要及时释放的资源,一律用using + Dispose

误区2:析构函数中操作托管对象

错误:GC 回收顺序不可控,其他托管对象可能已经被回收,直接报空引用异常

正确:析构中只处理非托管资源

误区3:所有类都写析构函数

错误:无非托管资源时,写析构会让对象进入终结队列,降低GC回收效率

正确:无原生资源,不写析构函数

误区4:手动调用析构函数

错误:C# 禁止手动调用 ~类名() 和 Finalize()

正确:资源释放统一用 Dispose


六、总结(博文核心干货)

  1. 析构函数是GC 自动兜底的非托管资源清理方法,执行时机不确定

  2. 语法:~类名(),无参无返回值,唯一不可重载

  3. 托管资源不用管,非托管资源必须靠Dispose+析构双保险

  4. 业务开发优先使用using 语法糖,杜绝手动 GC 操作

  5. 无原生非托管资源,坚决不写析构函数,避免性能损耗


码字不易,欢迎点赞收藏!后续持续更新 C# 底层原理、.NET 性能优化、实战避坑干货

http://www.jsqmd.com/news/1392251/

相关文章:

  • 2026肇庆自动门、旋转门五金配件去哪采购靠谱? - LYL仔仔
  • [基于AgentEvals的自动化评估-03]全面优化面向LangGraph的轨迹评估
  • Pixelle-Video 全自动短视频引擎指南:零基础 5 分钟生成第一条 AI 视频
  • 从英文劝退到全中文丝滑:Burp Suite 汉化启动器部署手记
  • Redash数据可视化入门:不写代码,如何把散落的数据变成一张实时看板?
  • MyBatis-Plus 的CRUD
  • 第24篇 · 从零到一,我的AI学习之路——复盘与给后来者的建议
  • B站视频下载终极指南:用免费开源的 BilibiliDown,一键把喜欢的视频全部抱回家
  • 告别蓝牙连接翻车:Blueman 蓝牙管理器三步上手指南
  • 半储能系统边缘接入去硬编码化:基于Node-RED流式引擎的免底层开发架构与代码实战
  • 猫抓 Cat-Catch 浏览器资源嗅探扩展:三步抓取网页视频,开源免费不折腾
  • 别再傻等 grep 卡 40 秒:用 KLOGG 日志分析工具三秒搜完 10GB 超大日志
  • 中小企业怎么用得起AI,奇点大会的低成本路径参考
  • Filebeat日志采集与ELK实战指南
  • 题解:瑞学堂 徐老师的零食分享队列
  • 第二篇 STM32MP157-M4:LiteOS-M 源码下载与工程搭建
  • 显卡涨价潮下,8卡RTX5090算力服务器市场前景与技术解析(2026)
  • 告别多套键鼠:用 Input Leap 实现跨设备键鼠共享的 4 步速通指南
  • MP4 文件打不开了?untrunc 视频修复全流程指南
  • ComfyUI中文工作流合集实战指南:50+即用模板,带你零基础快速上手AI创作
  • 办事急用找不到?档案存放证明怎么开?认准**渠道,就能轻松开具! - 实时传讯
  • 2026 线上少儿欧美外教一对一选课指南|5 家主流平台实测对比,避开报课误区!
  • scrcpy 免费投屏完整指南:10 分钟让安卓手机秒变电脑大屏
  • 深圳多家奢侈品回收横向测评,同款香奈儿 CF 报价参差不齐,核心差异一次性讲清 - 拾闻观天地
  • 快速生成 OpenCore EFI:OpCore-Simplify 新手完整上手指南
  • 题解:瑞学堂 徐老师的阶乘计算器
  • 揭秘江阳建设集团网站背后的匠心独运与未来愿景
  • Cat-Catch 浏览器扩展全攻略:一个下午搞定网页媒体资源捕获与 M3U8 合并
  • 星空芯片获头部新能源车企定点,地平线与博泰车联合作迈入量产落地新阶段
  • DeepSeek flash/pro如何接入CodeX