IDisposable接口详解

IDisposable接口详

1. MSDN中关于这个接口的说明

[ComVisible(true)]

public interface IDisposable

{

    // Methods

    void Dispose();

}

[ComVisible(true)]:指示该托管类型对 COM 是可见的。此接口的主要用途是释放非托管资源。当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存。但无法预测进行垃圾回收的时间。另外,垃圾回收器对窗口句柄或打开的文件和流等非托管资源一无所知。将此接口的Dispose方法与垃圾回收器一起使用来显式释放非托管资源。当不再需要对象时,对象的使用者可以调用此方法。

2. 基本应用

定义一个实现了IDisposable接口的类,代码如下:

public class CaryClass :IDisposable

{

   public void DoSomething()

   {

      Console.WriteLine("Do some thing....");

   }

   public void Dispose()

   {

      Console.WriteLine("及时释放资源");

   }

}

有两种方式来调用:

第一种方式,使用Using语句会自动调用Dispose方法,代码如下:

using (CaryClass caryClass = new CaryClass())

{

    caryClass.DoSomething();

}

第二种方式,现实调用该接口的Dispose方法,代码如下:

CaryClass caryClass = new CaryClass();

try

 {

    caryClass.DoSomething();              

 }

finally

 {

    IDisposable disposable = caryClass as IDisposable;

    if (disposable != null) disposable.Dispose();

 }

两种方式的执行结果是一样的,如下图:

3. Disposable 模式

在.NET种由于当对象变为不可访问后将自动调用Finalize方法,所以我们手动调用IDisposable接口的Dispose方法和对象终结器调用的方法极其类似,我们最好将他们放到一起来处理。我们首先想到的是重写Finalize方法,如下:

protected override void Finalize()

{

     Console.WritleLine("析构函数执行...");

}

当我们编译这段代码的时候,我们发现编译器会报如下的错误:

这是因为编译器彻底屏蔽了父类的Finalize方法,编译器提示我们如果要重写Finalize方法我们要提供一个析构函数来代替,下面我们就提供一个析构函数:

~CaryClass()

{

    Console.WriteLine("析构函数执行...");

}

实际上这个析构函数编译器会将其转变为如下代码:

protected override void Finalize()

{

   try

   {

     Console.WritleLine("析构函数执行...");

   }

   finally

   {

     base.Finalize();

   }

}

然后我们就可以将Dispose方法的调用和对象的终结器放在一起来处理,如下:

public class CaryClass: IDisposable

{

    ~CaryClass()

    {

        Dispose();

    }

    public void Dispose()

    {

        // 清理资源

    }

}

上面实现方式实际上调用了Dispose方法和Finalize方法,这样就有可能导致做重复的清理工作,所以就有了下面经典Disposable 模式:

private bool IsDisposed=false

public void Dispose() 

    Dispose(true); 

    GC.SuppressFinalize(this); 

protected void Dispose(bool Disposing) 

    if(!IsDisposed) 

    

        if(Disposing) 

        

           //清理托管资源

        

        //清理非托管资源

    

    IsDisposed=true

~CaryClass() 

    Dispose(false); 

}

备注:

  • SupressFinalize方法以防止垃圾回收器对不需要终止的对象调用 Object.Finalize()。
  • 使用IDisposable.Dispose 方法,用户可以在可将对象作为垃圾回收之前随时释放资源。如果调用了 IDisposable.Dispose 方法,此方法会释放对象的资源。这样,就没有必要进行终止。IDisposable.Dispose 应调用 GC.SuppressFinalize 以使垃圾回收器不调用对象的终结器。
  • 我们不希望Dispose(bool Diposing)方法被外部调用,所以他的访问级别为protected 。如果Diposing为true则释放托管资源和非托管资源,如果 Diposing等于false则该方法已由运行库从终结器内部调用,并且只能释放非托管资源。
  • 如果在对象被释放后调用其他方法,则可能会引发 ObjectDisposedException。

4. 实例解析

下面代码对Dispose方法做了封装,说明如何在使用托管和本机资源的类中实现 Dispose(bool) 的常规示例:

public class BaseResource : IDisposable

    {

        // 非托管资源

        private IntPtr handle;

        //托管资源

        private Component Components;

        // Dispose是否被调用

        private bool disposed = false;

        public BaseResource()

        {           

        }

       

        public void Dispose()

        {

            Dispose(true);           

            GC.SuppressFinalize(this);

        }

        protected virtual void Dispose(bool disposing)

        {

           

            if (!this.disposed)         

            {              

                if (disposing)

                {

                    // 释放托管资源

                    Components.Dispose();

                }

                // 释放非托管资源,如果disposing为false,

                // 只有托管资源被释放

                CloseHandle(handle);

                handle = IntPtr.Zero;

                // 注意这里不是线程安全的

            }

            disposed = true;

        }

        // 析构函数只会在我们没有直接调用Dispose方法的时候调用

        // 派生类中不用在次提供析构函数

        ~BaseResource()

        {

            Dispose(false);

        }

        // 如果你已经调用了Dispose方法后在调用其他方法会抛出ObjectDisposedException

        public void DoSomething()

        {

            if (this.disposed)

            {

                throw new ObjectDisposedException();

            }

        }

    }

    

    public class MyResourceWrapper : BaseResource

    {

        // 托管资源

        private ManagedResource addedManaged;

        // 非托管资源

        private NativeResource addedNative;

        private bool disposed = false;

       

        public MyResourceWrapper()

        {          

        }

        protected override void Dispose(bool disposing)

        {

            if (!this.disposed)

            {

                try

                {

                    if (disposing)

                    {                       

                        addedManaged.Dispose();

                    }

                    

                    CloseHandle(addedNative);

                    this.disposed = true;

                }

                finally

                {                  

                    base.Dispose(disposing);

                }

            }

        }

    }

使用CLR垃圾收集器,您不必再担心如何管理对托管堆分配的内存,不过您仍需清理其他类型的资源。托管类通过 IDisposable 接口使其使用方可以在垃圾收集器终结对象前释放可能很重要的资源。通过遵循 disposable 模式并且留 意需注意的问题,类可以确保其所有资源得以正确清理,并且在直接通过 Dispose 调用或通过终结器线程运行清理代码时 不会发生任何问题。

时间: 2024-12-12 20:49:19

IDisposable接口详解的相关文章

.NET深入解析LINQ框架(五:IQueryable、IQueryProvider接口详解)

阅读目录: 1.环路执行对象模型.碎片化执行模型(假递归式调用) 2.N层对象执行模型(纵横向对比链式扩展方法) 3.LINQ查询表达式和链式查询方法其实都是空壳子 4.详细的对象结构图(对象的执行原理) 5.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系 6.完整的自定义查询 1]. 环路执行对象模型.碎片化执行模型(假递归式调用) 这个主题扯的可能有点远,但是它关系着整个LINQ框架的设计结构,至少在我还没有搞懂LINQ的本意之前,在我脑海里一直频

Java6.0中Comparable接口与Comparator接口详解

Java6.0中Comparable接口与Comparator接口详解 说到现在,读者应该对Comparable接口有了大概的了解,但是为什么又要有一个Comparator接口呢?难道Java的开发者都吃饱撑着没事做吗? 再谈Comparator接口之前,大家应该先了解一个叫“策略模式”的东东.一下是百度百科对策略模式的描写: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化.(原文:The Strategy Pattern

Android Environment类的接口详解

Android应用开发中,常使用Environment类去获取外部存储目录,在访问外部存储之前一定要先判断外部存储是否已经是可使用(已挂载&可使用)状态, 并且需要在AndroidManifest.xml文件中添加外部存储读和写的权限. Environment类中提供了几个静态常量用于标识外部存储的状态,这些状态都是String类型 MEDIA_BAD_REMOVAL 在没有挂载前存储媒体已经被移除. MEDIA_CHECKING 正在检查存储媒体. MEDIA_MOUNTED 存储媒体已经挂载

JDBC常用接口详解

JDBC中常用接口详解 ***DriverManager 第一.注册驱动 第一种方式:DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 一.查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象. 二.程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦. 第二种方式:Class.forName("com.mysql.

嵌入式开发平台 迅为6818开发板接口详解

迅为八核iTOP-6818开发板接口详解: iTOP-4418接口图: iTOP-6818核心板: iTOP-6818开发板接口详解: 1.POWER电源接口 电源输入为5V/2A+,给核心板AXP228电源管理芯片提供5V电源,给底板供电. 2. SWITCH电源开关 轻触电源开关可以控制开发板电源通断. 3. 拨码开关 3位拨码开关控制4418启动模式. 4. 模数转换 A/D模数转换,调整滑动变阻器,模拟量可以通过转换口检测到. 5. JTAG接口 保留JTAG接口,有需要的用户可以进行扩

S?D?I?与?A?S?I 接口详解介绍

分量编码 在对彩色电视信号进行数字化处理和传输是,一种常用的方式是分别对其3个分量(Y,R-Y,B-Y)进行数字化编码.这就是分量分量编码,另外还有全信号编码,全信号编码是对彩色全电视信号直接进行编码形成数字视频信号.它的抽样频率一般采用fs=4fsc,这样对NTSC制和PAL制信号形成便于进行行间,场间,帧间的信号的正交抽样结构. 1. 抽样频率 当亮度信号Y的带宽为5.8MHz~6MHz,两个色差信号(R-Y)和(B-Y)的带宽均为2MHz是,可以获得满意的带宽. 2. 电视演播室分量编码国

基础拾忆------接口详解

目录: 基础拾忆------接口详解 基础拾忆------泛型详解 前言 接口定义了所有类继承接口时应遵循的契约.接口定义了 "要什么" ,派生类定义了 "怎么给" . 引用CLR VIA C#(类和接口继承) 在Microsoft.Net Framwork中,有一个名为System.Object的类,它定义了4个公共实例方法:ToString, Equals, GetHashCode和GetType.该类是其他所有类的根或者说最终基类.换言之,所有类都继承了Obj

“全栈2019”Java第八十三章:内部类与接口详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第八十三章:内部类与接口详解 下一章 "全栈2019"Java第八十四章:接口中嵌套接口详解 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学习小组&quo

抽象类及接口详解

一.前言 在上一节中我们讲到抽象类和接口之间的异同,我们一起回顾下其异同. 同: 1.都不可以被实例化 2.都含有声明但未实现的方法 3.都可以被继承 4.其子类必须实现其声明未实现的方法 异: 1.抽象类是多继承,接口是单继承 2.抽象类可以包含实现的方法,接口不能包含实现的方法 3.接口支持回调,抽象类不支持 4.抽象类更多的定义在一些类关系紧密的类间,接口则定义在实现其某一种功能之间 抽象类和接口的异同我们再次熟悉了一遍,今天我们主要讲的是抽象类和接口使用场景及详讲抽象类的使用方法及接口的