Finalize方法的问题:
- 不能确定它会在何时被调用。
- 由于它不是一个公有方法,我们不能显式调用它。
要提供显式释放或关闭对象的能力,一个类型通常要实现一种被称为Dispose的模式。所有定义了Finalize方法的类型都应该实现本节所描述的Dispose模式以给用户更多的控制权。
上节中OSHandle类实现的改进版,使用Dispose模式:
using
System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace DisposeTest
{
public sealed class OSHandle : IDisposable
{
// 释放非托管资源
[DllImport( " Kernel32 " )]
public extern static Boolean CloseHandle(IntPtr handle);
// 一个非托管资源的Win32句柄
private IntPtr handle;
// 构造器初始化handle句柄
public OSHandle(IntPtr handle)
{
this .handle = handle;
}
// 当垃圾收集执行时,下面的析构器(Finalize)方法将被调用
// 经编译后,该方法实际被更名为Finalize方法
~ OSHandle()
{
Dispose( false );
}
// 该方法可被显式调用来关闭非托管资源句柄
public void Dispose()
{
// 因为对象资源被显式清理,所以在这里阻止垃圾收集器调用Finalize方法
GC.SuppressFinalize( this );
// 进行实际的资源清理工作
Dispose( true );
}
// 该方法可替换Dispose方法
public void Close()
{
Dispose();
}
// 该方法可用于进行实际的清理工作。Finalize、Dispose、Close都要调用该方法。
// 因为该类是一个密封类,所以该方法定义为私有方法。
// 如果该类不是一个密封类,那么该方法应该是一个受保护方法。
private void Dispose( bool disposing)
{
// 同步那些同时调用Dispose/Close方法的线程
lock ( this )
{
if (disposing)
{
// 对象正在被显式释放/关闭,而非执行终止化。
// 因此在该法语句中访问那些引用其他对象的字段是安全的,
// 因为这些对象的Finalize方法还没有被调用。
// 对于OSHandle类由于未引用其他终止化对象,所以没什么可做的
}
// 对象正在被释放/关闭、或者被执行终止化
if (IsValid)
{
CloseHandle(handle);
}
// 将handle置为无效句柄以防止多次调用CloseHandle
handle = InvalidHandle;
}
}
// 无效句柄值
public IntPtr InvalidHandle { get { return IntPtr.Zero; } }
// 返回所封装的handle句柄
public IntPtr ToHandle() { return handle; }
// 稳式转型操作符用于返回所封装的handle句柄
public static implicit operator IntPtr(OSHandle osHandle)
{
return osHandle.ToHandle();
}
// 句柄是否有效
public bool IsValid { get { return handle != InvalidHandle; } }
public bool IsInvalid { get { return ! IsValid; } }
}
}
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace DisposeTest
{
public sealed class OSHandle : IDisposable
{
// 释放非托管资源
[DllImport( " Kernel32 " )]
public extern static Boolean CloseHandle(IntPtr handle);
// 一个非托管资源的Win32句柄
private IntPtr handle;
// 构造器初始化handle句柄
public OSHandle(IntPtr handle)
{
this .handle = handle;
}
// 当垃圾收集执行时,下面的析构器(Finalize)方法将被调用
// 经编译后,该方法实际被更名为Finalize方法
~ OSHandle()
{
Dispose( false );
}
// 该方法可被显式调用来关闭非托管资源句柄
public void Dispose()
{
// 因为对象资源被显式清理,所以在这里阻止垃圾收集器调用Finalize方法
GC.SuppressFinalize( this );
// 进行实际的资源清理工作
Dispose( true );
}
// 该方法可替换Dispose方法
public void Close()
{
Dispose();
}
// 该方法可用于进行实际的清理工作。Finalize、Dispose、Close都要调用该方法。
// 因为该类是一个密封类,所以该方法定义为私有方法。
// 如果该类不是一个密封类,那么该方法应该是一个受保护方法。
private void Dispose( bool disposing)
{
// 同步那些同时调用Dispose/Close方法的线程
lock ( this )
{
if (disposing)
{
// 对象正在被显式释放/关闭,而非执行终止化。
// 因此在该法语句中访问那些引用其他对象的字段是安全的,
// 因为这些对象的Finalize方法还没有被调用。
// 对于OSHandle类由于未引用其他终止化对象,所以没什么可做的
}
// 对象正在被释放/关闭、或者被执行终止化
if (IsValid)
{
CloseHandle(handle);
}
// 将handle置为无效句柄以防止多次调用CloseHandle
handle = InvalidHandle;
}
}
// 无效句柄值
public IntPtr InvalidHandle { get { return IntPtr.Zero; } }
// 返回所封装的handle句柄
public IntPtr ToHandle() { return handle; }
// 稳式转型操作符用于返回所封装的handle句柄
public static implicit operator IntPtr(OSHandle osHandle)
{
return osHandle.ToHandle();
}
// 句柄是否有效
public bool IsValid { get { return handle != InvalidHandle; } }
public bool IsInvalid { get { return ! IsValid; } }
}
}
注意:调用Close或Dispose方法只是在一个确定的时刻对对象占用的非托管资源执行清理操作而已,它们并不会控制托管堆中对象所使用的内存的生存期。这意味着我们在调用Dispose或Close方法后,仍然可以调用对象上的方法,只是操作很可能会失败而已。
使用using语句可以简化使用Dispose模式对象的try{...a = new A();...}finally{...a.Dispose();...}语法。
using语句支持初始化多个变量的能力,前提是这些变量的类型相同;using还支持使用一个已经初始化的变量。
我们只应在需要资源清理的地方调用Dispose或Close方法,以防止对象被过早的释放内存。