释放非托管资源 IDisposable模式的实现、使用

  与c++一样,c#可以定义析构函数。但c#的析构函数主要用于释放非托管资源。在Net中,由GC垃圾回收线程掌握对象资源的释放,程序员无法掌控析构函数的调用时机。为了完全掌控非托管资源的释放,Net提供一个IDisposable接口。

一、c#的析构函数

1.1 定义析构函数

    class Program
    {
        static void Main(string[] args)
        {

        }

        ~Program()//析构函数
        { }
    }

 1.2 编译器把析构函数解析成Finalize终结器方法

Finalize的具体实现

.method family hidebysig virtual instance void 
        Finalize() cil managed
{
  // Code size       14 (0xe)
  .maxstack  1
  .try
  {
    IL_0000:  nop
    IL_0001:  nop
    IL_0002:  leave.s    IL_000c
  }  // end .try
  finally
  {
    IL_0004:  ldarg.0
    IL_0005:  call       instance void [mscorlib]System.Object::Finalize()
    IL_000a:  nop
    IL_000b:  endfinally
  }  // end handler
  IL_000c:  nop
  IL_000d:  ret
} // end of method Program::Finalize

 如同构造函数一样,析构函数会调用基类的析构函数。与构造函数相反的是,析构函数先完成自己的清理工作,再调用基类的析构函数。

二、IDisposable模式的实现

如何安全、快速的释放非托管资源?如果我们的类使用的非托管资源,如数据库连接、文件句柄,这些资源需做到:使用后立刻释放。如果我们只在析构函数实现释放,则无法达到使用后立刻释放这个目标。为了实现这个目标,Net引入了IDisposable模式。

 IDisposable的接口定义如下

    public interface IDisposable
    {
        // Summary:
        //     Performs application-defined tasks associated with freeing, releasing, or
        //     resetting unmanaged resources.
        void Dispose();
    }

 我们来分析一下SqlConnection类的IDisposable模式的实现

SqlConnection继承于DbConnection , DbConnection继承于ComponentComponent里实现了

IDisposable接口,Component代码如下:

public class Component : MarshalByRefObject, IComponent, IDisposable
{

    // Methods
    public void Dispose()
    {
        this.Dispose(true);////释放托管资源
        GC.SuppressFinalize(this);//请求系统不要调用指定对象的终结器. //该方法在对象头中设置一个位,系统在调用终结器时将检查这个位
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            lock (this)
            {
                if ((this.site != null) && (this.site.Container != null))
                {
                    this.site.Container.Remove(this);
                }
                if (this.events != null)
                {
                    EventHandler handler = (EventHandler) this.events[EventDisposed];
                    if (handler != null)
                    {
                        handler(this, EventArgs.Empty);
                    }
                }
            }
        }
    }

    ~Component()
    {
        this.Dispose(false);//有可能开发人员忘记调用Dispose方法,导致资源泄漏,为了避免这种情况,在析构函数调用Dispose,确保资源被释放
    }

   
}

 在SqlConnection中重写了protected virtual void Dispose(bool disposing)方法,代码如下

 protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            this._userConnectionOptions = null;//把本对象引用的其他对象设置成空
            this._poolGroup = null;
            this.Close();//关闭数据库连接
        }
        this.DisposeMe(disposing);
        base.Dispose(disposing);//调用基类的Dispose
    }

 三、正确调用Dispose方法

        SqlConnection conn = new SqlConnection();
            //do something;
            conn.Dispose();

 这代码有漏洞,当do something出现异常时,导致Dispose执行不了,更安全的做法是:

    class Program
    {
        static void Main(string[] args)
        {
            SqlConnection conn = new SqlConnection();
            try
            {
                //do something;
            }
            finally
            {
                conn.Dispose();
            }
        }
    }

 为了简化代码量,c#使用using简化输入,编译器自动翻译成 try...finally

    class Program
    {
        static void Main(string[] args)
        {
            using(SqlConnection conn = new SqlConnection())
            {
                //do something;
            }
        }
    }

 

posted @ 2012-04-11 14:53  虎头  阅读(2355)  评论(0编辑  收藏  举报