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

参考资料

SafeHandle 和 Dispose的更多相关文章

  1. SafeHandle和Dispose z

    SafeHandle最大的意义是封装一个托管资源且本身会执行.NET中的资源释放模式(所谓的Dispose Pattern),这样,开发者在使用非托管资源时,不可以不需要执行繁琐的资源释放模式,而直接 ...

  2. Finalize()、Dispose()、SafeHandle、GC

    Finalize https://msdn.microsoft.com/en-us/library/system.object.finalize%28v=vs.110%29.aspx https:// ...

  3. safehandle 和析构函数

    safehandle 是一种析构机制,她和析构函数有什么分别. 首先要理解析构函数.析构函数在.net中是没有顺序的,因此你不能假定另一个对象的析构函数在你之后运行,哪怕它是你的成员!如果你的成员也有 ...

  4. 第七节:使用实现了dispose模式的类型

    知道类型如何实现dispose模式之后,接下来看一下开发人员怎样使用提供了dispose模式的类型.这里不再讨论前面的SafeHandle类,而是讨论更常用的FileStream类. 可以利用File ...

  5. Implementing a Dispose method

    [Implementing a Dispose method] 1.实现System.IDsiposable.Dispose()方法.不能将此方法设置为virtual,子类不能override此方法. ...

  6. C#之Dispose

    前言 谈到Dispose,首先需要理解C#的资源 资源类型 托管资源:由CLR创建和释放 非托管资源:资源的创建和释放不由CLR管理.比如IO.网络连接.数据库连接等等.需要开发人员手动释放. 如何释 ...

  7. 【C# .Net GC】清除非托管类型(Finalize终结器、dispose模式以及safeHandler)

    总结 1.一般要获取一个内核对象的引用,最好用SafeHandle来引用它,这个类可以帮你管理引用计数,而且用它引用内核对象,代码更健壮 2.托管中生成并引用非托管,一但非托管和托管中的引用断开(托管 ...

  8. Delphi的分配及释放---New/Dispose, GetMem/FreeMem及其它函数的区别与相同

    转载自:http://www.cnblogs.com/qiusl/p/4028437.html?utm_source=tuicool 我估摸着内存分配+释放是个基础的函数,有些人可能没注意此类函数或细 ...

  9. JS魔法堂:定义页面的Dispose方法——[before]unload事件启示录

    前言  最近实施的同事报障,说用户审批流程后直接关闭浏览器,操作十余次后系统就报用户会话数超过上限,咨询4A同事后得知登陆后需要显式调用登出API才能清理4A端,否则必然会超出会话上限.  即使在页面 ...

  10. C#学习笔记---Dispose(),Finalize(),SuppressFinalize

    http://www.cnblogs.com/eddyshn/archive/2009/08/19/1549961.html 在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Fina ...

随机推荐

  1. 利用 Page Visibility API 优化网页性能与用户体验

    在现代 Web 开发中,用户可能会频繁切换标签页,或让网页处于后台运行.为了避免不必要的资源浪费并提升用户体验,合理利用 Page Visibility API 可以在页面不可见时暂停或减少资源的消耗 ...

  2. 《Vue.js 设计与实现》读书笔记 - 第13章、异步组件与函数式组件

    第13章.异步组件与函数式组件 13.1 异步组件要解决的问题 用户可以简单通过 import 异步导入组件. <template> <component :is="asy ...

  3. vue 赶鸭子上架入门笔记(一) 安装开发环境

    准备接手一个 vue 的前端项目,从零开始学习 vue.目标不高大上,能看得懂代码,能进行简单的修改,改完能打包和部署. 首先解决 vue 开发环境的准备.访问 Node.js 官方网站,下载适合你操 ...

  4. 通过一个题目三种变式讲清楚go接口类型断言

    [第一种]一种类型实现多个接口,各个接口变量调用各自的方法 type Work struct { i int } func (w Work) ShowA() int { return w.i + 10 ...

  5. NICE与静态优先级的关系

    在Linux系统中,nice值和静态优先级用于控制进程调度的优先级,但它们的范围和含义有所不同.让我们详细解释一下两者的区别和联系. 1. Nice值 范围:nice值的范围是从 -20 到 19. ...

  6. 同步完善Docker常用操作命令

    镜像 images_name 表示镜像名 con_name表示容器名 #获取镜像 docker pull images_name #查看已有的docker镜像 docker images #删除镜像 ...

  7. 关于sizeof、strlen的理解与辨析以及strlen的模拟实现

    关于sizeof.strlen的理解与辨析以及strlen的模拟实现 1. sizeof() sizeof的作用: 计算变量所占内存内存空间大小,单位是字节. 注意事项: ① 首先要明确的一点是:si ...

  8. CMake 生成器表达式---条件表达式和逻辑运算符

    [写在前面] CMake 的生成器表达式用于在构建系统级别上进行条件判断和逻辑运算,它们通常用在目标属性和生成器表达式上下文中.这些表达式允许你根据不同的平台.配置或编译器来定制构建过程. 本文引用的 ...

  9. Rust 版本一直是 1.4 或者其它版本

    Rust 版本一直是 1.4 或者其它版本 通过rustup update 升级或者 rustup default 设置版本也不行 解决方法 删除 rust-toolchain 这个东西,这个东西覆盖 ...

  10. 3.19 Linux命令的执行过程是怎样的?(新手必读)

    前面讲过,在 Linux 系统中"一切皆文件",Linux 命令也不例外.那么,当编辑完成 Linux 命令并回车后,系统底层到底发生了什么事情呢? 简单来说,Linux 命令的执 ...