接上一篇.net框架读书笔记---CLR内存管理\垃圾收集(二),主要学习了终止化对象(实现了Finalize方法的对象),了解了终止化对象的弊端,学习了通过实现IDisposable接口,通过Dispose方法来清理非托管资源,从而减轻垃圾收集器的压力,本节继续学习。
一、使用实现了Dispose模式的类型
System.IO.FileStream其基类Stream实现了IDisposable接口:
class Program
{
static void Main( string [] args)
{
// 创建要写入临时文件的字节
byte [] b = new byte [] { 1 , 2 , 3 , 4 , 5 };
FileStream fs = new FileStream( " temp.dat " , FileMode.Create);
// 写入临时文件
fs.Write(b, 0 , b.Length);
fs.Close();
// fs.Write(b, 0, b.Length); // 该行会爆出异常,试图写入关闭的文件
File.Delete( " temp.dat " );
}
}
上面的代码如果File.Delete之前不调用close是会报错的,delete之前必须保证文件已经关闭。注意调用CLose方法后FileStream对象仍然在托管堆中,最后垃圾收集器会运行,并将该FileStream对象判定为可收集的垃圾。这时垃圾收集器本来应该调用FileStream对象上的Finalize方法,但是因为Dispose方法调用了GC的SuppressFinalize方法,所以Finalize方法不再被调用,对象的内存直接被回收。
二、C#的using语句
上面的例子应该使用try/catch块中如下:
class Program
{
static void Main( string [] args)
{
// 创建要写入临时文件的字节
byte [] b = new byte [] { 1 , 2 , 3 , 4 , 5 };
FileStream fs = new FileStream( " temp.dat " , FileMode.Create);
try
{
// 写入临时文件
fs.Write(b, 0 , b.Length);
}
catch
{
// do someting
}
finally
{
fs.Close();
}
// fs.Write(b, 0, b.Length); // 该行会爆出异常,试图写入关闭的文件
File.Delete( " temp.dat " );
}
}
对于上述代码C#提供了更为简洁的语法,可以使用using语句
class Program
{
static void Main( string [] args)
{
// 创建要写入临时文件的字节
byte [] b = new byte [] { 1 , 2 , 3 , 4 , 5 };
using (FileStream fs = new FileStream( " temp.dat " , FileMode.Create))
{
// 写入临时文件
fs.Write(b, 0 , b.Length);
}
// fs.Write(b, 0, b.Length); // 该行会爆出异常,试图写入关闭的文件
File.Delete( " temp.dat " );
}
}
首先在using语句内初始化一个对象,并将其引用保存在一个变量内。然后在using大括号内访问该变量。当编译这段代码,编译器会自动创建一个try和finalily块。在finally内编译器会调用其Dispose方法,显然,using语句只能用于那些实现了IDisposable接口的类型。
上面代码的IL为
.method private hidebysig static void Main( string [] args) cil managed
{
.entrypoint
// 代码大小 76 (0x4c)
.maxstack 4
.locals init ([ 0 ] uint8[] b,
[ 1 ] class [mscorlib]System.IO.FileStream fs,
[ 2 ] bool CS$ 4 $ 0000 )
IL_0000: nop
IL_0001: ldc.i4. 5
IL_0002: newarr [mscorlib]System.Byte
IL_0007: dup
IL_0008: ldtoken field valuetype ' <PrivateImplementationDetails>{7FD0DAA5-56D7-474F-B21D-5AECDF07BAFB} ' / ' __StaticArrayInitTypeSize=5 ' ' <PrivateImplementationDetails>{7FD0DAA5-56D7-474F-B21D-5AECDF07BAFB} ' :: ' $$method0x6000001-1 '
IL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray( class [mscorlib]System.Array,
valuetype [mscorlib]System.RuntimeFieldHandle)
IL_0012: stloc. 0
IL_0013: ldstr " temp.dat "
IL_0018: ldc.i4. 2
IL_0019: newobj instance void [mscorlib]System.IO.FileStream::.ctor( string ,
valuetype [mscorlib]System.IO.FileMode)
IL_001e: stloc. 1
. try
{
IL_001f: nop
IL_0020: ldloc. 1
IL_0021: ldloc. 0
IL_0022: ldc.i4. 0
IL_0023: ldloc. 0
IL_0024: ldlen
IL_0025: conv.i4
IL_0026: callvirt instance void [mscorlib]System.IO.Stream::Write(uint8[],
int32,
int32)
IL_002b: nop
IL_002c: nop
IL_002d: leave.s IL_003f
} // end .try
finally
{
IL_002f: ldloc. 1
IL_0030: ldnull
IL_0031: ceq
IL_0033: stloc. 2
IL_0034: ldloc. 2
IL_0035: brtrue.s IL_003e
IL_0037: ldloc. 1
IL_0038: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003d: nop
IL_003e: endfinally
} // end handler
IL_003f: nop
IL_0040: ldstr " temp.dat "
IL_0045: call void [mscorlib]System.IO.File::Delete( string )
IL_004a: nop
IL_004b: ret
} // end of method Program::Main
很明显可以看到try/finally,刚才试了一下,using不能处理异常的,看上面的IL会发现,其没有catch,感觉很不爽。using也不能滥用,避免过早的调用了dispose。导致应用程序产生异常(ObjectDisposeException)