0

0

C# IDisposable接口实现方法 C#如何正确实现IDisposable

幻夢星雲

幻夢星雲

发布时间:2026-03-18 08:21:49

|

534人浏览过

|

来源于php中文网

原创

必须实现IDisposable当类直接持有非托管资源或封装了IDisposable托管对象;否则易致内存泄漏或句柄耗尽,而仅含普通引用类型无需实现。

c# idisposable接口实现方法 c#如何正确实现idisposable

什么时候必须实现 IDisposable

当你的类直接持有非托管资源(如文件句柄、数据库连接、Socket、GDI对象)或封装了实现了 IDisposable 的托管对象(如 FileStreamHttpClientDbContext),就必须实现 IDisposable。否则资源可能长期滞留,引发内存泄漏或句柄耗尽。

常见误判:仅含普通引用类型字段(如 List<string>Dictionary<int, object>)不需要实现 IDisposable —— GC 会自动回收它们。

Dispose() 方法的标准写法(带 finalizer 的完整模式)

只有当你**直接持有非托管资源**时,才需要写 finalizer(即析构函数)。绝大多数情况只需“托管清理”,用不着 finalizer —— 它会拖慢 GC,且执行时机不可控。

标准结构如下(注意命名、访问修饰符和空检查):

public class ResourceManager : IDisposable
{
    private bool _disposed = false;
    private IntPtr _nativeHandle = IntPtr.Zero; // 示例:非托管句柄
    private FileStream _fileStream;

    public ResourceManager()
    {
        _fileStream = new FileStream("data.bin", FileMode.Open);
        _nativeHandle = AllocateUnmanagedResource();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // 清理托管资源
            _fileStream?.Dispose();
        }

        // 清理非托管资源
        if (_nativeHandle != IntPtr.Zero)
        {
            FreeUnmanagedResource(_nativeHandle);
            _nativeHandle = IntPtr.Zero;
        }

        _disposed = true;
    }

    ~ResourceManager()
    {
        Dispose(false);
    }
}
  • Dispose(bool) 是核心逻辑,disposing == true 表示可安全访问托管对象
  • 必须调用 GC.SuppressFinalize(this),避免 finalizer 再次触发
  • 务必检查 _disposed 标志,防止重复释放(尤其 finalizer 可能和显式 Dispose() 并发)
  • 非托管资源释放后应置为无效值(如 IntPtr.Zero),避免二次释放崩溃

只用托管资源?跳过 finalizer,用 usingDisposeAsync 更合适

如果你的类只包装了 HttpClientMemoryStreamTimer 等托管 IDisposable 对象,无需 finalizer —— 这是高频错误。

此时只需轻量实现:

SongAI
SongAI

免费AI歌曲和音乐生成平台,支持文字生成歌曲、AI歌词创作、AI翻唱等功能

下载
public class DataProcessor : IDisposable
{
    private readonly HttpClient _httpClient = new HttpClient();
    private readonly Timer _timer = new Timer();

    public void Dispose()
    {
        _httpClient?.Dispose();
        _timer?.Dispose();
    }
}

更推荐做法:让调用方用 using 块控制生命周期,而不是依赖 finalizer兜底。finalizer 不是保险丝,是延迟释放的隐患。

若涉及异步 I/O(如 StreamWriter 写磁盘、HttpClient.SendAsync),优先实现 IAsyncDisposable,配合 await using

public class AsyncDataWriter : IAsyncDisposable
{
    private readonly StreamWriter _writer;

    public AsyncDataWriter(Stream stream) => _writer = new StreamWriter(stream);

    public async ValueTask DisposeAsync()
    {
        await _writer.FlushAsync();
        _writer.Dispose();
    }
}

容易被忽略的三个坑

这些点不报错,但会导致资源泄漏或线程不安全:

  • Dispose() 后继续访问已释放字段(如调用 Write()ObjectDisposedException)—— 应在每个公共方法开头加 if (_disposed) throw new ObjectDisposedException(GetType().Name);
  • 子类继承可释放类但未重写 Dispose(bool),导致子类新增的资源无法释放 —— 记得把基类 Dispose(bool) 声明为 virtual,子类用 override
  • Dispose() 中调用虚方法(如 OnDisposed())—— 此时对象可能处于部分析构状态,虚方法分发可能出错;改用非虚回调或事件

最稳妥的做法:只在 Dispose(bool)disposing == true 分支里操作托管对象,非托管资源清理逻辑保持纯函数式、无虚调用、无异常抛出(或至少用 try/catch 吞掉)。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1091

2023.08.02

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

848

2023.08.22

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1091

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

619

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

355

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

235

2025.08.29

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

2037

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

682

2025.10.17

抖漫入口地址合集
抖漫入口地址合集

本专题整合了抖漫入口地址相关合集,阅读专题下面的文章了解更多详细地址。

17

2026.03.17

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 11.6万人学习

C 教程
C 教程

共75课时 | 5.6万人学习

C++教程
C++教程

共115课时 | 22.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号