C#如何释放未托管资源

在C#里面有2种机制来释放未托管资源:

  • 声明一个析构函数(或终结器),作为类的一个成员
  • 在类中执行System.IDisposable接口

析构函数

下面这段代码是一段带有析构函数的简单代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; 

namespace MemRelease
{
    class Program
    {
        ~Program()
        {
            // Orders.
        } 

        static void Main(string[] args)
        {
        }
    }
} 

在IL DASM中,你会发现并没有这个析构的方法。C#编译器在编译析构函数时,会隐式地把析构函数的代码编译为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 

是一个try…finally的结构,

try

{

// destructor implementation

}

finally

{

base.Finalize();

}

~Program()析构函数中执行的代码封装在Finalize()方法的一个try块中。对父类Finalize()方法的调用放在finally块中,确保该调用的执行。

使用析构函数来释放资源有几个问题:

  1. 与C++析构函数相比,C#析构函数的问题是他们的不确定性。在删除C++对象时,其析构函数会立即执行,但是由于垃圾收集器的工作方式,无法确定C#对象的析构函数何时执行。
  2. C#析构函数的执行会延迟对象最终从内存中删除的时间。有析构函数的对象需要2次处理才能删除:第一次调用析构函数时,没有删除对象,第二次调用才真正删除对象。


IDisposable接口

在C#中,推荐使用System.IDisposable接口替代析构函数。IDisposable接口定义一个模式,为释放未托管的资源提供了确定的机制,并避免产生析构函数固有的与垃圾函数器相关的问题。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MemRelease
{
    class Program : IDisposable
    {
        public void Dispose()
        {
            // implementation
        }        

        static void Main(string[] args)
        {
        }
    }
}

假定有一个类ResourceGobbler,它使用某些外部资源,且执行IDisposable接口。如果要实例化着各类的实例,使用它,然后释放它,就可以使用下面的代码。

ResourceGobbler theInstance = new ResoucrGobbler();

// do your processing

theInstance.Dispose();

如果加入异常处理:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MemRelease
{
    public class ResourceGobbler : IDisposable
    {
        public void Dispose()
        {
            //implementation
        }
    }

    class Program 
    {        
        static void Main(string[] args)
        {
            ResourceGobbler theInstance = null;

            try
            {
                theInstance = new ResourceGobbler();
                // do your processing
            }
            finally
            {
                if (theInstance != null)
                {
                    theInstance.Dispose();
                } 
            }
        }
    }
}

即使在处理过程中出现异常,这个版本也可以确保总是在theInstance上调用Dispose(),总能释放有theInstance使用的资源。

C#提供了一种语法,可以确保执行IDisposal接口的对象的引用超出作用域时,在该对象上自动调用Dispose().

using (ResourceGobbler theInstance =  new ResourceGobbler());
{
    // do your processing
}
伪python爱好者,正宗测试实践者。
原文地址:https://www.cnblogs.com/herbert/p/1750194.html