当谈到垃圾回收,在C#中,托管资源的垃圾回收是通过CLR的Garbage Collection来实现的,Garbage Collection会调用堆栈上对象的析构函数完成对象的释放工作;而对于一些非托管资源,比如数据库链接对象等,需要实现IDisposable接口进行手动的垃圾回收。那么什么时候使用Idisposable接口,以及如何使用呢?

public interface IDisposable
{
    void Dispose();
}

public class DisposablClass : IDisposable
{
    //是否回收完毕
    bool _disposed;

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

    ~DisposableClass()
    {
        Dispose(false);
    }

    //这里的参数表示示是否需要释放那些实现IDisposable接口的托管对象
    protected virtual void Dispose(bool disposing)
    {
        if(_disposed) return; //如果已经被回收,就中断执行
        if(disposing)
        {
            //TODO:释放那些实现IDisposable接口的托管对象
        }

        //TODO:释放非托管资源,设置对象为null

        _disposed = true;
    }
}

Dispose()方法

当需要回收非托管资源的DisposableClass类,就调用Dispoase()方法。而这个方法不会被CLR自动调用,需要手动调用。

~DisposableClass(),析构函数

当托管堆上的对象没有被其它对象引用,GC会在回收对象之前,调用对象的析构函数。这里的~DisposableClass()析构函数的意义在于告诉GC你可以回收我,Dispose(false)表示在GC回收的时候,就不需要手动回收了。

虚方法Dispose(bool disposing)

通过此方法,所有的托管和非托管资源都能被回收。参数disposing表示是否需要释放那些实现IDisposable接口的托管对象。

如果disposings设置为true,就表示DisposablClass类依赖某些实现了IDisposable接口的托管对象,可以通过这里的Dispose(bool disposing)方法调用这些托管对象的Dispose()方法进行回收。

如果disposings设置为false,就表示DisposableClass类依赖某些没有实现IDisposable的非托管资源,那就把这些非托管资源对象设置为null,等待GC调用DisposableClass类的析构函数,把这些非托管资源进行回收。

另外,以上把Dispose(bool disposing)方法设置为protected virtual的原因是希望有子类可以一起参与到垃圾回收逻辑的设计,而且还不会影响到基类。比如有这样的一个子类:

public class SubDisposableClass : DiposableClass
{
    private bool _disposed; //表示是否已经被回收

    protected override void Dispose(bool disposing)
    {
        if(!_disposed) //如果还没有被回收
        {
            if(disposiing) //如果需要回收一些托管资源
            {
                //TODO:回收托管资源,调用IDisposable的Dispose()方法就可以
            }

            //TODO:回收非托管资源,把之设置为null,等待CLR调用析构函数的时候回收

            _disposed = true;
        }

        base.Dispose(disposing);//再调用父类的垃圾回收逻辑
    }
}


在.NET 2.0之前,如果一个对象的析构函数抛出异常,这个异常会被CLR忽略。但.NET 2.0以后,如果析构函数抛出异常就会导致应用程序的崩溃。所以,保证析构函数不抛异常变得非常重要。

还有,Dispose()方法允许抛出异常吗?答案是否定的。如果Dispose()方法有抛出异常的可能,那就需要使用try/catch来手动捕获。以下是考虑Dispose()方法有异常可能的写法:

public class DisposableClass : IDisposable
{
    bool _disposed;

    ......

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

        if(disposing)
        {
            //TODO:调用托管资源的Dispose()方法进行垃圾回收
        }

        try
        {
            _channelFactory.Close(); //关闭的时候可能会有异常
        }
        catch(Exception ex)
        {
            _log.Warn(ex);//记录日志

            try
            {
                _channelFactory.Abort();//丢弃的时候可能会有异常
            }
            catch(Exception cex)
            {
                _log.Warn(cex);//记录日志
            }
        }

        _channelFactory = null;
        _disposed = true;
    }
}


总结:当我们自定义的类及其业务逻辑中引用某些托管和非托管资源,就需要实现IDisposable接口,实现对这些资源对象的垃圾回收。

参考资料:

http://www.cnblogs.com/lori/p/3535470.html
http://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/

C#中的IDisposable模式的更多相关文章

  1. C#中对IDisposable接口的理解

    http://blog.sina.com.cn/s/blog_8abeac5b01019u19.html C#中对IDisposable接口的理解 本人最近接触一个项目,在这个项目里面看到很多类实现了 ...

  2. (转)MVC中的Repository模式

    1.首先创建一个空的MVC3应用程序,命名为MyRepository.Web,解决方案命名为MyRepository. 2.添加一个类库项目,命名为MyRepository.DAL,添加一个文件夹命名 ...

  3. MVC中的Repository模式

    1.首先创建一个空的MVC3应用程序,命名为MyRepository.Web,解决方案命名为MyRepository. 2.添加一个类库项目,命名为MyRepository.DAL,添加一个文件夹命名 ...

  4. C#中的IDisposable接口

    深入理解C#中的IDisposable接口 写在前面 在开始之前,我们需要明确什么是C#(或者说.NET)中的资源,打码的时候我们经常说释放资源,那么到底什么是资源,简单来讲,C#中的每一种类型都是一 ...

  5. 【转】C#中对IDisposable接口的理解

    IDisposable接口定义:定义一种释放分配的资源的方法. .NET 平台在内存管理方面提供了GC(Garbage Collection),负责自动释放托管资源和内存回收的工作,但它无法对非托管资 ...

  6. [转]改善C#程序的建议4:C#中标准Dispose模式的实现

    需要明确一下C#程序(或者说.NET)中的资源.简单的说来,C#中的每一个类型都代表一种资源,而资源又分为两类: 托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象: 非托管资源:不 ...

  7. 制作类似ThinkPHP框架中的PATHINFO模式功能

    一.PATHINFO功能简述 搞PHP的都知道ThinkPHP是一个免费开源的轻量级PHP框架,虽说轻量但它的功能却很强大.这也是我接触学习的第一个框架.TP框架中的URL默认模式即是PathInfo ...

  8. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

  9. 4.在MVC中使用仓储模式进行增删查改

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-using-the-repository-pattern-in-mvc/ 系列目录: ...

随机推荐

  1. KMP模板及总结

    KMP是一种字符串匹配算法,它在时间复杂度上较暴力匹配算法由很大的优势.比如我要找字符串S中是否存在子串P,如果暴力匹配的话,则时间复杂度为O(n*m),而kmp算法时间复杂度为O(n+m). 这里我 ...

  2. php安装amqp扩展

    1.要安装AMQP PHP扩展,必须先安装librabbitmq库 1.1使用以下步骤下载并安装库: # 下载 git clone git://github.com/alanxz/rabbitmq-c ...

  3. C#socket编程序(二)

    在上一篇中,我列了一些常用的方法,可以说这些方法是一些辅助性的方法,对于分析网络中的主机属性非常有用.在这篇中,我将会介绍一下面向连接(TCP)socket编程,其中辅以实例,代码可供下载. 对于TC ...

  4. ABP zero 3.2 发布

    v3.2.0 (2017-03-07) Common Tenant based UI customizations (allow tenants to upload custom CSS and lo ...

  5. bzoj 2752

    2752 思路: 线段树: 代码: #include <cstdio> #include <cstring> #include <iostream> #includ ...

  6. day5模块学习 -- time、datetime时间模块

    1.定义 模块:用来从逻辑上组织python(变量,函数,类,逻辑:实现一个功能)代码,本质就是.py结尾的python文件(文件名:test.py,对应的模块名test) 包:用来从逻辑上组织模块的 ...

  7. codeforces 603 A

    题目大意:给你一个0,1串, 你可以反转一段连续的区间,问你最长的合法子串是多长, 合法字串相邻的两个不能相同. 思路:dp[ i ][ k ][ j ] 表示到第 i 个字符, 处于k这种状态, k ...

  8. 基于 Laravel 开发博客应用系列 —— 从测试开始(二):使用Gulp实现自动化测试

    3.使用 Gulp 进行 TDD(测试驱动开发) Gulp 是一个使用 JavaScript 编写的自动化构建工具.用于对前端通用任务(如最小化.压缩.编译)进行自动构建.Gulp 还可以用来监控源代 ...

  9. HDU - 1022 Train Problem I STL 压栈

    Train Problem I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  10. 浅谈 Java 中的枚举

    枚举也就是一一列举,常用来表示那些可以明确范围的集合,比方说性别,季节,星期,月份等. 在 JDK 1.5 才出现枚举类,在没有出现枚举类之前,我们要表示几个确定的值通常会使用常量来表示,形如 pub ...