当谈到垃圾回收,在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. Oracle学习笔记:parallel并行处理

    在使用oracel查询时,可以通过并行提高查询速度.例如: ) from table_name a; 强行启用并行度来执行当前SQL.加上这个说明之后,可以强行启用Oracle的多线程处理功能,提高效 ...

  2. MySQL学习笔记:Engine存储引擎

    在使用Mysql建表过程中,有时候会遇到一些奇怪的现象.例如,如何插入数据就是查询不到数据,此时可能是建表的存储引擎设置成为engine=blackhole的原因. 1.engine=innodb 提 ...

  3. **汇总CodeIgniter(CI)的数据库操作函数

    //查询: $query = $this->db_query("SELECT * FROM table"); ================================ ...

  4. head命令 tail命令

    head命令 head命令用于显示文件的开头的内容.在默认情况下,head命令显示文件的头10行内容. -n<数字>:指定显示头部内容的行数: -c<字符数>:指定显示头部内容 ...

  5. spring-boot分环境打包为jar包

    1.pom配置 <!-- 多环境打包 start --> <profiles> <!-- 开发环境配置 --> <profile> <id> ...

  6. ZooKeeper实践:(1)集群管理

    前言: 随着业务的扩大,用户的增多,访问量的增加,单机模式已经不能支撑,从而出现了从单机模式->垂直应用模式->集群模式,集群模式诞生了,伴随着一堆问题也油然而生,Master怎么选举,机 ...

  7. mongodb优化篇

    在掌握了mongo的体系结构和基本操作后,开始学习 mongodb的优化,由于资源有限,只能网络上整理一些资料,我大致理解的mongo的优化分为以下几步:  1.监控 mongodb可以通过profi ...

  8. HTTP Status 500 - Request processing failed; nested exception is org.apache.ibatis.binding.BindingException

    在使用Maven工程管理工具整合SSM框架时,Mybatis使用逆向工程生成的pojo,mapper接口及映射文件,把mapper接口和映射文件放在DAO工程的同一级src/main/java目录下. ...

  9. 8-8 Ddfense Line uva1471 优先级队列

    题意:给你一串长度为n的序列   你的任务是删除一个连续的子序列  使得剩下的序列中有一个长度最大的连续递增子序列  例如  将 5 3 4 9 2 8 6 7 1 中的9 2 8 删除  得到5 3 ...

  10. 005 爬虫(requests与beautifulSoup库的使用)

    一:知识点 1.安装requests库 2.Brautiful soup 可以提供一些简单的,python式的函数来处理导航,搜索,修改分析树等功能. 她是一个工具箱,通过解析文档为用户提供需要抓去的 ...