.net非托管资源

常见的非托管资源包括数据库链接、文件句柄、网络链接、互斥体、COM对象、套接字、位图和GDI+对象等

 

非托管资源的清理,主要有两种方式:Finalize 方法和 Dispose 方法,这两种方法提供了在垃圾收集执行前进行资源清理的方法。Finalize 方式,又称为终止化操作,其大致的原理为:通过对自定义类型实现一个Finalize 方法来释放非托管资源,而终止化操作在对象的内存回收之前通过调用 Finalize方法来释放资源;Dispose 模式,指的是在类中实现 IDisposable 接口,该接口中的Dispose 方法定义了显式释放由对象引用的所有非托管资源。因此,Dispose 方法提供了更加精确的控制方式,在使用上更加的灵活

 

在继承链中所有实例将递归调用 base.Finalize 方法,也就是意味调用终结器释放资

源时,将释放所有的资源,包括父类对象引用的资源。因此,在 C#中,也无需调用或重写Object.Finalize 方法,事实上显示的重写会引发编译时错误,只需实现虚构函数即可。

 

 

{

重写了Finalize方法的类型对象,其引用类型对象的代龄将被提升,从而带来内存压力。

l  Finalize方法在某些情况下可能不被执行,例如可能某个终结器被无限期的阻止,则其他终结器得不到调用。因此,应该确保重写的 Finalize方法尽快被执行。

基于以上原因,应该避免重写 Finalize 方法,而实现 Dispose 模式来完成对非托管资源的清理操作,具体实现见下文描述。

对于Finalize 方法,有以下规则值得总结:

C#中无法显示的重写Finalize方法,只能通过析构函数语法形式来实现。

l  struct中不允许定义析构函数,只有 class中才可以,并且只能有一个。

l  Finalize方法不能被继承或重载。

析构函数不能加任何修饰符,不能带参数,也不能被显示调用,唯一的例外是在子类重写时,通过base调用父类Finalize方法,而且这种方式也被隐式封装在析构函数中。

执行垃圾回收之前系统会自动执行终止化操作。

l  Finalize方法中,可以实现使得被清理对象复活的机制,不过这种操作相当危险,而且没有什么实际意义,仅作参考,不推荐使用:对于重写了Finalize 方法的类型来说,可以通过GC. SuppressFinalize 来免除终结。对于Finalize 方式来说,存在如下几个弊端,因此一般情况下在自定义类型中应避免重写 Finalize 方法,这些弊端主要包括:

终止化操作的时间无法控制,执行顺序也不能保证。因此,在资源清理上不够灵活,也可能由于执行顺序的不确定而访问已经执行了清理的对象。

l  Finalize方法会极大地损伤性能,GC使用一个终止化队列的内部结构来跟踪具有 Finalize方法的对象。当重写了Finalize方法的类型在创建时,要将其指针添加到该终止化队列中,由此对性能产生影响;另外,垃圾回收时调用 Finalize方法将同时清理所有的资源,包括其父类对象的资源,也是影响性能的一个因素。

}

{

在该模式中,公有Dispose方法通过调用重载虚方法 Disposebool disposing)方法来实现,具体的资源清理操作实现于虚方法中。两种策略的区别是:disposing参数为真时,Dispose方法由用户代码调用,可释放托管或者非托管资源;disposing参数为假时,Dispose方法由Finalize调用,并且只能释放非托管资源。

l  disposed字段,保证了两次调用Dispose方法不会抛出异常,值得推荐。

派生类中实现Dispose模式,应该重写基类的受保护Dispose方法,并且通过base调用基类的Dispose方法,以确保释放继承链上所有对象的引用资源,在整个继承层次中传播 Dispose模式。

protected override void Dispose(bool disposing)

{

    if (!disposed)

    {

        try

        {

            //子类资源清理

            //......

            disposed = true;

        }

        finally

        {

            base.Dispose(disposing);

        }

    }

}

另外,基于编程习惯的考虑,一般在实现Dispose方法时,会附加实现一个Close方法来达到同样的资源清理目的,而 Close内部其实也是通过调用Dispose来实现的。    }

    //实现一个处理资源清理的具体方法

    protected virtual void Dispose(bool disposing)

    {

        if (! disposed)

        {

            if (disposing)

            {

                //清理托管资源

            }

            //清理非托管资源

            if (_handle != IntPtr.Zero)

            {

                //执行资源清理,在此为关闭对象句柄

                CloseHandle(_handle);

                _handle = IntPtr.Zero;

            }          

        }

        disposed = true;             

    }

    public void Close()

    {

        //在内部调用Dispose来实现

        Dispose();

    }

}

在上述实现Dispose模式的典型操作中,有几点说明:

l  Dispose方法中,应该使用 GC. SuppressFinalize防止 GC调用Finalize方法,因为显式调用Dispose显然是较佳选择。

公有Dispose方法不能实现为虚方法,以禁止在派生类中重写。Dispose模式

另一种非托管资源的清理方式是Dispose 模式,其原理是定义的类型必须实现 System.IDisposable接口,该接口中定义了一个公有无参的 Dispose 方法,用户可以在该方法中实现对非托管资源的清理操作。在此,我们实现一个典型的Dispose模式:

class MyDispose : IDisposable

{

    //定义一个访问外部资源的句柄

    private IntPtr _handle;

    //标记Dispose是否被调用

    private bool disposed = false;

    //实现IDisposable接口

    public void Dispose()

    {

        Dispose(true);

        //阻止GC调用Finalize方法

        GC.SuppressFinalize(this);

}

posted on 2013-11-06 08:49  sajiao  阅读(2092)  评论(0编辑  收藏  举报

导航

街边网