C#之Dispose
前言
谈到Dispose,首先需要理解C#的资源
资源类型
- 托管资源:由CLR创建和释放
- 非托管资源:资源的创建和释放不由CLR管理。比如IO、网络连接、数据库连接等等。需要开发人员手动释放。
如何释放
调用的是微软类库或者第三方类库,一般类库会提供释放的方法,即约定为Dispose,调用即可。
为啥一定是Dispose方法?
每个类库当然可以提供各自释放资源的方法,比如close()、close()、release()、clear()等等。
但为了统一,微软提供了IDispose接口,其中只声明了一个void的Dispose()方法。而且还为实现了IDispose接口的类提供了using释放资源的语法糖。
看代码:
namespace System
{
[ComVisible(true)]
public interface IDisposable
{
//执行与释放或重置非托管资源关联的应用程序定义的任务。
void Dispose();
}
}
忘记释放怎么办?
比如我们进行一个给图片加水印的功能,使用System.Drwing类库中的Image对象。写代码的时候,我们既不手动调用Dispose方法,也不使用using语法。那么Image对象就一直会留在内存中吗?
当然不会,Image类有析构函数,在其中调用了Dispose方法。
上源码:
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (this.nativeImage != IntPtr.Zero)
{
try
{
SafeNativeMethods.Gdip.GdipDisposeImage(new HandleRef(this, this.nativeImage));
}
catch (Exception ex)
{
if (ClientUtils.IsSecurityOrCriticalException(ex))
{
throw;
}
}
finally
{
this.nativeImage = IntPtr.Zero;
}
}
}
~Image()
{
this.Dispose(false);
}
既然最终析构函数中会释放资源,那么我们是不是没必要手动释放了呢?
这就要说说析构函数了
析构函数
当一个类的实例被GC回收的时候,最终调用的方法。它和构造函数正好相反,后者是在类的实例初始化时调用。
它的写法是这样子的:
class Car
{
~Car() // finalizer
{
// cleanup statements...
}
}
隐式的调用了基类的Finalize方法,所以等价下面的写法:
protected override void Finalize()
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}
Finalize操作呢,有以下限制:
- The exact time when the finalizer executes is undefined.
- The finalizers of two objects are not guaranteed to run in any specific order, even if one object refers to the other.
- The thread on which the finalizer runs is unspecified.
(来源:https://docs.microsoft.com/en-us/dotnet/api/system.object.finalize?view=netframework-4.8 )
简单理解就是Finalize操作由GC决定,回收的时间不定、顺序不定、线程不定。所以析构函数中调用Dispose只是一个保险措施,并不能代替手动释放资源。
比如数据库连接,你打开连接不及时释放,很快就无法连接新的数据库了。而此时GC有可能还未执行析构函数。
当然,析构函数在GC回收的时候,还会因为垃圾回收机制有其他性能问题,详细我们在垃圾回收机制的文章中讲。
(参考:https://www.viva64.com/en/b/0437/ )
Dispose模式
所以,到目前为止,我们清楚的知道,对于非托管资源的使用,一定要记得释放资源。
我们给被人提供类库的时候,也明白了到底什么时候需要实现IDispose接口了。
当然,Dispose的实现已然有套路了,称之为Dispose模式,以下是示例:
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
class BaseClass : IDisposable
{
// Flag: Has Dispose already been called?
bool disposed = false;
// Instantiate a SafeHandle instance.
SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
handle.Dispose();
// Free any other managed objects here.
//
}
disposed = true;
}
}
哟,刚才上面的Image类里面不就是这么写的么?
是呀!
C#之Dispose的更多相关文章
- Delphi的分配及释放---New/Dispose, GetMem/FreeMem及其它函数的区别与相同
转载自:http://www.cnblogs.com/qiusl/p/4028437.html?utm_source=tuicool 我估摸着内存分配+释放是个基础的函数,有些人可能没注意此类函数或细 ...
- JS魔法堂:定义页面的Dispose方法——[before]unload事件启示录
前言 最近实施的同事报障,说用户审批流程后直接关闭浏览器,操作十余次后系统就报用户会话数超过上限,咨询4A同事后得知登陆后需要显式调用登出API才能清理4A端,否则必然会超出会话上限. 即使在页面 ...
- Finalize()、Dispose()、SafeHandle、GC
Finalize https://msdn.microsoft.com/en-us/library/system.object.finalize%28v=vs.110%29.aspx https:// ...
- C#学习笔记---Dispose(),Finalize(),SuppressFinalize
http://www.cnblogs.com/eddyshn/archive/2009/08/19/1549961.html 在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Fina ...
- delphi.memory.分配及释放---New/Dispose, GetMem/FreeMem及其它函数的区别与相同
我估摸着内存分配+释放是个基础函数,有些人可能没注意此类函数或细究,但我觉得还是弄明白的好. 介绍下面内存函数前,先说一下MM的一些过程,如不关心可忽略: TMemoryManager = recor ...
- Dispose() C# 优化内存
public void Dispose() { ((IDisposable)_designer).Dispose(); } #region IDisposable Support private bo ...
- Close与Dispose的区别
Close与Dispose的区别: Close 是停业整顿,停业了,可以通过公关,再重开,物还是原来的物:只是关闭而已,没有释放真正的释放资源,可以重新打开:Close是关门Dispose是破产: D ...
- C#中标准Dispose模式的实现与使用(条目17 实现标准的销毁模式)
实现了Dispose模式与实现了IDisposable接口的区别就是:IDisposable的实现的可靠性(释放相关资源)要靠编程人员来解决(你确信你从来都一直调用了Dispose(Close)方法吗 ...
- 类中实现 Dispose And Finalize
1.Dispose方法中,应该使用GC.SuppressFinalize防止GC调用Finalize方法,因为显示调用Dispose比较好. 2.Disposed字段保证了两次调用Dispose方法不 ...
- System.IO.File.Create 不会自动释放,一定要Dispose
这样会导致W3P进程一直占用这个文件 System.IO.File.Create(HttpContext.Current.Server.MapPath(strName)) 最好加上Dispose Sy ...
随机推荐
- Cloudera Manager添加主机节点
为了监控方便,想把研发环境中的主机节点都纳入Cloudera Manager的管理中,这样在遇到问题时可方便的查看主机的硬件资源情况. 添加主机节点有多种方式,由于我是离线工作,所以选择rpm包的方式 ...
- DOS的FOR命令用法总结
鉴于dos自带的关于for命令的帮助信息看起来太简单,自己总结了一下,并增加了必要的实例,以备日后自己查阅.其中一些地方可能存在理解错误,敬请指出. [转发请注明出处]
- 物理STANDBY库创建还原点(打开为read write后再变回主库)
开启STANDBY库为READ WRITE 1.取消主库传送归档 SQL> alter system set log_archive_dest_state_2=defer; 2.取消备库应用日志 ...
- QT串口助手(二):参数配置
作者:zzssdd2 E-mail:zzssdd2@foxmail.com 一.前言 主要实现功能 串口参数的配置:波特率.数据位.停止位.校验位 本机串口设备的查询与添加显示 串口设备的手动更新与打 ...
- Spring Aop中四个重要概念,切点,切面,连接点,通知
1. 通知: 就是我们编写的希望Aop时执行的那个方法.我们通过Aop希望我们编写的方法在目标方法执行前执行,或者执行后执行.2. 切点:切点就是我们配置的满足我们条件的目标方法.比如我们规定:名字前 ...
- Mybatis SQL映射文件详解
Mybatis SQL映射文件详解 mybatis除了有全局配置文件,还有映射文件,在映射文件中可以编写以下的顶级元素标签: cache – 该命名空间的缓存配置. cache-ref – 引用其它命 ...
- 三路握手 三报文握手 TIME_WAIT three way handshake three message handshake
UNIX网络编程卷1:套接字联网API(第3版) 第2章 传输层:TCP.UDP和SCTP 2.4 TCP TCP不能被描述成100%可靠的协议 数次重传失败,则放弃 数据的可靠递送和故障的可靠通知 ...
- 解决windows git乱码问题
在windows中打开git bash git config --global i18n.commitencoding utf-8 设置提交日志使用utf-8 git config --g ...
- .NET5 它来了!微软大一统时代来临!
今天双11,Microsoft released.NET 5(在他们的开发博客上同时发布).新版本的重点是改进.NET Core 3.1: 更小的单文件应用程序.对 Windows ARM64的支持以 ...
- STM32 定时器详细篇(基于HAL库)
l 16位的向上.向下.向上/向下(中心对齐)计数模式,支持自动重装载 l 16位的预分频器 l 每个定时器都有多个独立通道,每个通道可用于 * 输入捕获 * 输出比较 * PWM输出 * ...