在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块中,确保该调用的执行。
使用析构函数来释放资源有几个问题:
- 与C++析构函数相比,C#析构函数的问题是他们的不确定性。在删除C++对象时,其析构函数会立即执行,但是由于垃圾收集器的工作方式,无法确定C#对象的析构函数何时执行。
- 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 }