SafeHandle 和 Dispose
SafeHandle 和 Dispose
这是从 https://www.cnblogs.com/zeroone/p/3708112.html 复制过来的,原文的格式不够好,重新排版一下。
SafeHandle 最大的意义是封装一个托管资源且本身会执行.NET中的资源释放模式(所谓的Dispose Pattern),这样,开发者在使用非托管资源时,不需要执行繁琐的资源释放模式,而直接使用 SafeHandle 就可以了,另外 SafeHandle 继承自 CriticalFinalizerObject 类型,CLR 对其的 Finalize 方法有特殊优化。
整个.NET Framework 内有许多 SafeHandle,比如 .NET 4 新加的 SafeBuffer(在System.Runtime.InteropServices命名空间内),使用它可以申请非托管内存资源。这样的话就不需要直接使用 Marshal.AllocHGlobal 和FreeHGlobal 方法了。
举一个简单的例子,使用GDI中的CreateSolidBrush返回一个非托管资源Handle,如果需要在类型中使用这个Handle,则要注意正确的资源释放,如下代码:
using System.Runtime.InteropServices;
// 使用 GDI 中 CreateSolidBrush 中的资源
public class MyPen : IDisposable
{
bool _isDisposed;
IntPtr _gdiBrush;
public MyPen()
{
_gdiBrush = CreateSolidBrush(0);
}
publicvoid Dispose()
{
Dispose(true);
// 不需要析构函数再次运行了
GC.SuppressFinalize(this);
}
//析构函数
~MyPen()
{
Dispose(false);
}
//protected Dispose,被公共Dispose和析构函数调用
protected virtual void Dispose(bool disposing)
{
if(_isDisposed)
{
return;
}
if (disposing)
{
//释放托管资源
}
// 释放非托管资源
if (_gdiBrush != IntPtr.Zero)
{
DeleteObject(_gdiBrush);
_gdiBrush = IntPtr.Zero;
}
_isDisposed =true;
}
#region Win32 API
[DllImport("gdi32.dll")]
static extern IntPtr CreateSolidBrush(uint crColor);
[DllImport("gdi32.dll", EntryPoint ="DeleteObject")]
staticexternbool DeleteObject([In] IntPtr hObject);
#endregion
}
而如果用 SafeHandle 类型定义的话,使用起非托管资源就方便多了。
首先,创建一个类型继承自 Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid,改写ReleaseHandle 方法来释放非托管资源。如下代码:
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
class SafeBrushHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeBrushHandle(IntPtr handle): base(true)
{
base.SetHandle(handle);
}
protected override bool ReleaseHandle()
{
//判断 Handle 合法
if (!this.IsInvalid)
{
//释放资源
return DeleteObject(this.handle);
}
return true;
}
[DllImport("gdi32.dll", EntryPoint ="DeleteObject")]
static extern bool DeleteObject([In] IntPtr hObject);
}
然后使用这个 SafeHandle,我们的 MyPen 类型的执行就简单多了:
using System.Runtime.InteropServices;
// 使用 SafeBrushHandle
public class MyPen : IDisposable
{
bool _isDisposed;
SafeBrushHandle _gdiBrush;
public MyPen()
{
_gdiBrush = new SafeBrushHandle( CreateSolidBrush(0) );
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
_gdiBrush.Dispose();
_isDisposed =true;
}
#region Win32 API
[DllImport("gdi32.dll")]
static extern IntPtr CreateSolidBrush(uint crColor);
#endregion
}
所以,使用 SafeHandle 可以让使用非托管资源类型的定义更加简洁,因为不需要再定义析构函数了。非托管资源被 SafeHandle 包装好后,整个资源对象会表现起来像一个托管的对象(注意是表现起来像,本质上显然不是),即便是忘了调用它的Dispose 方法,非托管资源也会随着 SafeHandle 被垃圾回收时而释放,因为 SafeHandle 的析构函数(Finalize方法)会调用内部的 ReleaseObject 方法的。