冠军

导航

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 方法的。

参考资料

posted on 2023-06-26 12:47  冠军  阅读(24)  评论(0编辑  收藏  举报