在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
}