前言

谈到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的更多相关文章

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

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

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

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

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

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

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

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

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

    我估摸着内存分配+释放是个基础函数,有些人可能没注意此类函数或细究,但我觉得还是弄明白的好. 介绍下面内存函数前,先说一下MM的一些过程,如不关心可忽略: TMemoryManager = recor ...

  6. Dispose() C# 优化内存

    public void Dispose() { ((IDisposable)_designer).Dispose(); } #region IDisposable Support private bo ...

  7. Close与Dispose的区别

    Close与Dispose的区别: Close 是停业整顿,停业了,可以通过公关,再重开,物还是原来的物:只是关闭而已,没有释放真正的释放资源,可以重新打开:Close是关门Dispose是破产: D ...

  8. C#中标准Dispose模式的实现与使用(条目17 实现标准的销毁模式)

    实现了Dispose模式与实现了IDisposable接口的区别就是:IDisposable的实现的可靠性(释放相关资源)要靠编程人员来解决(你确信你从来都一直调用了Dispose(Close)方法吗 ...

  9. 类中实现 Dispose And Finalize

    1.Dispose方法中,应该使用GC.SuppressFinalize防止GC调用Finalize方法,因为显示调用Dispose比较好. 2.Disposed字段保证了两次调用Dispose方法不 ...

  10. System.IO.File.Create 不会自动释放,一定要Dispose

    这样会导致W3P进程一直占用这个文件 System.IO.File.Create(HttpContext.Current.Server.MapPath(strName)) 最好加上Dispose Sy ...

随机推荐

  1. [CPP] 智能指针

    介绍 C++ 的智能指针 (Smart Pointers) 相关 API. C++ 中的智能指针是为了解决内存泄漏.重复释放等问题而提出的,它基于 RAII (Resource Acquisition ...

  2. oracle创建恢复编录(recovery catalog)

    1.在要作为恢复编录的数据库创建用户 create user rman identified by oracle default tablespace system temporary TABLESP ...

  3. oracle 12C单实例打PSU

    前提: oracle不管打什么样的补丁,readme都是很好的参考资料. Oracle每季度都会更新一个最新的PSU,现在12.1.0.2.0的最新的PSU是Patch 26925311. 由于今天白 ...

  4. BAPI创建PO,禁止净价信息更新

    大家都知道创建PO时,我们如果勾选了"信息更新",则该PO保存后相应的信息记录会把这个PO更新为其最后的凭证,那么这张PO的净价会作为下次创建新PO时净价的默认值. 这样我们设置的 ...

  5. MySQL数据库基础知识及优化

    MySQL数据库基础知识及优化必会的知识点,你掌握了多少? 推荐阅读: 这些必会的计算机网络知识点你都掌握了吗 关于数据库事务和锁的必会知识点,你掌握了多少? 关于数据库索引,必须掌握的知识点 目录 ...

  6. 记录一下 ThreadLocal 与 WeakReference

    ThreadLocal & WeakReference Thread整体的模块图 Thread -> ThreadLocalMap 对于继承了 WeakReference Entry本身 ...

  7. Nginx(七):location的使用以及nginx关闭原理

    上一篇中,我们了解了如何nginx的配置原则及解析框架,以及解析location配置的具体实现,相信大家对该部分已经有了比较深刻的认识. 本篇,我们进一步来了解下,解析之后的配置,如何应用到实际中的吧 ...

  8. mysql主从复制安装配置

    mysql主从复制安装配置 基础设置准备 #操作系统: centos6.5 #mysql版本: 5.7 #两台虚拟机: node1:192.168.182.111(主) node2:192.168.1 ...

  9. Canvas实现弧线时钟

    最近试着用canvas元素的2d绘图函数做了一个弧线形的时钟. 我也没啥好说的,直接上代码: <div id="myclock"></div> <sc ...

  10. loj10007线段

    题目描述 数轴上有 n 条线段,选取其中 k 条线段使得这 k 条线段两两没有重合部分,问 k 最大为多少. 输入格式 第一行为一个正整数 n: 在接下来的 n 行中,每行有 2 个数 a_i,b_i ...