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. ftrace options 中的irq-info

    /sys/kernel/debug/tracing/options/irq_info 是 ftrace 中的一个选项,用于启用或禁用有关中断的详细信息的跟踪. options/irq_info 的具体 ...

  2. 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-2-环境准备与搭建-基于Maven(详细教程)

    1.简介 上一篇宏哥已经讲解和分享了如何通过引入jar包来搭建Java+Playwright自动化测试环境,这一种是比较老的方法,说白了就是过时的老古董,但是我们必须了解和知道,其实maven搭建无非 ...

  3. 云原生周刊:CNCF 宣布 Cilium 毕业 | 2023.10.16

    开源项目推荐 Reloader Reloader 是一个 Kubernetes 控制器,用于监控 ConfigMap 和 Secrets 中的变化,并对 Pod 及其相关部署.StatefulSet. ...

  4. javascript 中==和===的区别【转载】

     javascript 中==和===的区别[转]     对于JavaScript中比较运算符,可能大家用的比较多的是"==".对于"==="很多人可能很陌生 ...

  5. IDEA久违了!FeignX插件支持方法级别的导航跳转

    > 需求调研:发现身边的同事追踪业务代码的时候,如果遇见feign接口,大家都是通过全局搜url进一步找到远程服务的实现.一旦项目大起来,路径变得复杂甚至重名之后,这将会是灾难. 市面上并没有很 ...

  6. 浅谈TiKV集群运维问题排查与修复——磁盘空间占用问题

    作者:来自 vivo 互联网存储团队- Yuan Jianwei 本文介绍了 TiKV 磁盘空间问题的排查思路与解决方案. 一.背景介绍 在业务快速扩张的年代,vivo 内部的很多业务为了可以快速上线 ...

  7. 3.21 Linux PATH环境变量及作用(初学者必读)

    在讲解 PATH 环境变量之前,首先介绍一下 which 命令,它用于查找某个命令所在的绝对路径.例如: [root@localhost ~]# which rm /bin/rm [root@loca ...

  8. 2-6 C/C++ 编写头文件

    目录 头文件怎么起作用 避免头文件被重复引用 避免头文件被重复引用的方法:条件编译 1. 给每个头文件添加一个预编译变量(preprocessor variable)作为标记(Label) 2. 使用 ...

  9. C#验证IP是否为局域网地址的三种方法

    C#验证IP是否为局域网地址的三种方法 前一阵子有[广州.NET群]的客户问起这个问题,说他们需要验证客户输入的网站是否为局域网.其实局域网的IP并没有确定的定义,只要是局域网中,即可设置为任何一个I ...

  10. dephi winspy Demo

    在spy+中未找到这样的功能 有个隐藏窗口的进程,相让窗口显示出来,虽然可以找到进程ID,然后再逐步找到主窗口句柄,但又没句柄发各种消息的软件. 计得以前有个窗口精灵之类的,网上找了很多,要么没有此功 ...