析构函数和Dispose的使用区别
老生常谈的问题了,MSDN也有非常详细的说明但看起来不是很系统。也曾经做过分析,但没有总结下来又忘了,这次整理一下MSDN和网上搜集的一些资料,以备不时只需。
下面是MSDN对这两个函数的建议使用方法
MSDN建议
// Design pattern for a base class.
public class Base : IDisposable
{
//保证重复释放资源时系统异常
private bool _isDisposed = false; // 析构函数,编译器自动生成Finalize()函数由GC自动调用,保证资源被回收。
// 最好不要声明空析构函数,造成性能问题
// 如果没有引用非托管资源就不需要显示声明析构函数,会造成性能问题,系统会自动生成默认析构函数
~Base()
{
// 此处只需要释放非托管代码即可,因为GC调用时该对象资源可能还不需要释放
Dispose(false);
} //外部手动调用或者在using中自动调用,同时释放托管资源和非托管资源
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); ///告诉GC不需要再次调用
} protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
//释放托管资源
}
// 释放非托管资源
// 释放大对象 this._isDisposed = true;
} } }
下面是通过Reflector工具对上面代码反射出来的结果,可以看出析构函数直接被翻译成Finalize()函数了,因为Finalize函数不能被重写,所以只能用析构函数的方式实现Finalize方法。
Reflector反射结果
public class Base : IDisposable
{
// Fields
private bool _isDisposed; // Methods
public Base();
public void Dispose();
protected virtual void Dispose(bool disposing);
protected override void Finalize();
}
在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Finalize。Finalize的目的是用于释放非托管的资源,而Dispose是用于释放所有资源,包括托管的和非托管的。
在这个模式中,void Dispose(bool disposing)函数通过一个disposing参数来区别当前是否是被Dispose()调用。如果是被Dispose()调用,那么需要同时释放 托管和非托管的资源。如果是被~Base()(也就是C#的Finalize())调用了,那么只需要释放非托管的资源即可。
这是因为,Dispose()函数是被其它代码显式调用并要求释放资源的,而Finalize是被GC调用的。在GC调用的时候Base所引用的其它托管对象可能还不需要被销毁,并且即使要销毁,也会由GC来调用。因此在Finalize中只需要释放非托管资源即可。另外一方面,由于在 Dispose()中已经释放了托管和非托管的资源,因此在对象被GC回收时再次调用Finalize是没有必要的,所以在Dispose()中调用 GC.SuppressFinalize(this)避免重复调用Finalize。
然而,即使重复调用Finalize和Dispose也是不存在问题的,因为有变量_isDisposed的存在,资源只会被释放一次,多余的调用会被忽略过去。因此,上面的模式保证了:
1、 Finalize只释放非托管资源;
2、 Dispose释放托管和非托管资源;
3、 重复调用Finalize和Dispose是没有问题的;
4、 Finalize和Dispose共享相同的资源释放策略,因此他们之间也是没有冲突的。
微软对Dispose和Finalize方法使用准则
Finalize
下面的规则概括了 Finalize 方法的使用准则:
1、不能在结构中定义析构函数。只能对类使用析构函数。
2、一个类只能有一个析构函数。
3、无法继承或重载析构函数。
4、无法调用析构函数。它们是被自动调用的。
5、析构函数既没有修饰符,也没有参数。
- 仅在要求终结的对象上实现 Finalize。存在与 Finalize 方法相关的性能开销。
- 如果需要 Finalize 方法,应考虑实现 IDisposable,以使类的用户可以避免因调用 Finalize 方法而带来的开销。
- 不要提高 Finalize 方法的可见性。该方法的可见性应该是 protected,而不是 public。
- 对象的 Finalize 方法应该释放该对象拥有的所有外部资源。此外,Finalize 方法应该仅释放由该对象控制的资源。Finalize 方法不应该引用任何其他对象。
- 不要对不是对象的基类的对象直接调用 Finalize 方法。在 C# 编程语言中,这不是有效的操作。
- 应在对象的 Finalize 方法中调用基类的 Finalize 方法。
注意
基类的 Finalize 方法通过 C# 和 C++ 析构函数语法自动进行调用。
释放
下面的规则概括了 Dispose 方法的使用准则:
- 在封装明确需要释放的资源的类型上实现释放设计方案。用户可以通过调用公共 Dispose 方法释放外部资源。
- 在通常包含控制资源的派生类型的基类型上实现释放设计方案,即使基类型并不需要也如此。如果基类型有 Close 方法,这通常指示需要实现 Dispose。在这类情况下,不要在基类型上实现 Finalize 方法。应该在任何引入需要清理的资源的派生类型中实现 Finalize。
- 使用类型的 Dispose 方法释放该类型所拥有的所有可释放资源。
- 对实例调用了 Dispose 后,应通过调用 GC.SuppressFinalize 方法禁止 Finalize 方法运行。此规则的一个例外是当必须用 Finalize 完成 Dispose 没有完成的工作的情况,但这种情况很少见。
- 如果基类实现了 IDisposable,则应调用基类的 Dispose 方法。
- 不要假定 Dispose 将被调用。如果 Dispose 未被调用,也应该使用 Finalize 方法释放类型所拥有的非托管资源。
- 当资源已经释放时,在该类型上从实例方法(非 Dispose)引发一个 ObjectDisposedException。该规则不适用于 Dispose 方法,该方法应该可以在不引发异常的情况下被多次调用。
- 通过基类型的层次结构传播对 Dispose 的调用。Dispose 方法应释放由此对象以及此对象所拥有的任何对象所控制的所有资源。例如,可以创建一个类似 TextReader 的对象来控制 Stream 和 Encoding,两者均在用户不知道的情况下由 TextReader 创建。另外,Stream 和 Encoding 都可以获取外部资源。当对 TextReader 调用 Dispose 方法时,TextReader 应继而对 Stream 和 Encoding 调用 Dispose,使它们释放其外部资源。
- 考虑在调用了某对象的 Dispose 方法后禁止对该对象的使用。重新创建已释放的对象是难以实现的方案。
- 允许 Dispose 方法被调用多次而不引发异常。此方法在首次调用后应该什么也不做。
下面是CSDN高手总结
1、Finalize方法(C#中是析构函数,以下称析构函数)是用于释放非托管资源的,而托管资源会由GC自动回收。所以,我们也可以这样来区分 托管和非托管资源。所有会由GC自动回收的资源,就是托管的资源,而不能由GC自动回收的资源,就是非托管资源。在我们的类中直接使用非托管资源的情况很 少,所以基本上不用我们写析构函数。
2、大部分的非托管资源会给系统带来很多负面影响,例如数据库连接不被释放就可能导致连接池中的可用数据库连接用尽。文件不关闭会导致其它进程无法读写这个文件等等。
实现模型:
1、由于大多数的非托管资源都要求可以手动释放,所以,我们应该专门为释放非托管资源公开一个方法。实现IDispose接口的Dispose方法是最好的模型,因为C#支持using语句快,可以在离开语句块时自动调用Dispose方法。
2、虽然可以手动释放非托管资源,我们仍然要在析构函数中释放非托管资源,这样才是安全的应用程序。否则如果因为程序员的疏忽忘记了手动释放非托管资源, 那么就会带来灾难性的后果。所以说在析构函数中释放非托管资源,是一种补救的措施,至少对于大多数类来说是如此。
3、由于析构函数的调用将导致GC对对象回收的效率降低,所以如果已经完成了析构函数该干的事情(例如释放非托管资源),就应当使用SuppressFinalize方法告诉GC不需要再执行某个对象的析构函数。
4、析构函数中只能释放非托管资源而不能对任何托管的对象/资源进行操作。因为你无法预测析构函数的运行时机,所以,当析构函数被执行的时候,也许你进行操作的托管资源已经被释放了。这样将导致严重的后果。
5、(这是一个规则)如果一个类拥有一个实现了IDispose接口类型的成员,并创建(注意是创建,而不是接收,必须是由类自己创建)它的实例对象,则 这个类也应该实现IDispose接口,并在Dispose方法中调用所有实现了IDispose接口的成员的Dispose方法。
只有这样的才能保证所有实现了IDispose接口的类的对象的Dispose方法能够被调用到,确保可以手动释放任何需要释放的资源。
————————————————————————————————————————
一个人的时候,总是在想
我的生活到底在期待什么……
析构函数和Dispose的使用区别的更多相关文章
- 析构函数和Dispose方法的区别
1. 析构函数(Finalize)只能释放非托管资源, 它是由GC调用. 2. Dispose方法可以释放托管资源和非托管资源,它是由用户手动调用的. 在Dispose()中调用 GC.Suppres ...
- C#析构函数与Dispose
有几种不同的操作方式 方式一: namespace ConsoleApp1 { class Test { ~Test()// 析构函数 { ...
- 析构函数virtual与非virtual区别 [转]
作为通常的原则,如果一个类定义了虚函数,那么它的析构函数就应当是virtual的.因为定义了虚函数则隐含着:这个类会被继承,并且会通过基类的指针指向子类对象,从而得到多态性. 这个类可能会被继承, ...
- .net 的析构函数和dispose模式
- 【C#】C#中方法(函数)的类型有哪些
目录结构: contents structure [+] 构造函数 引用类型的构造函数 值类型的构造函数 析构函数 析构函数的使用 析构函数和Dispose()方法的区别 操作符重载 转化操作符方法 ...
- C#中Dispose、析构函数、close的区别
一.Close与Dispose这两种方法的区别 调用完了对象的Close方法后,此对象有可能被重新进行使用:而Dispose方法来说,此对象所占有的资源需要被标记为无用了,也就是此对象要被销毁,不能再 ...
- C#中Dispose,finalize,GC,析构函数区别
释放类所使用的未托管资源的两种方式: 1.利用运行库强制执行的析构函数,但析构函数的执行是不确定的,而且,由于垃圾收集器的工作方式,它会给运行库增加不可接受的系统开销. 2.IDisposable接 ...
- 内存回收,Dispose,Close,Finalie(C#中的析构函数)
NET中的资源分托管和非托管,所谓的托管是指CLR(通用语言运行时)中进行管理的资源,它可以由CLR自动进行内存回收. 也就是大家熟知的GC(垃圾回收机制). 而对于 非托管资源,比如数据库连接,CO ...
- 深入解析Close()和Dispose()的区别
很多人都认为Close()方法内部会调用Dispose()方法,所以并没有本质的区别!实际上这个看法不是很准确,对有 些类来说,的确Close()和Dispose()没有本质区别,但是对有些类来说并非 ...
随机推荐
- 大家一起写mvc(二)
上一篇已经看了,我想大家都明白了mvc的原理,今天我们来说一下要写自己mvc框架必须要会的技术. mvc的目录是这样的 src目录是我们核心的mvc代码.这个代码明天讲,今天主要讲的代码都在test目 ...
- 在线测试 ssl 安全性
记录下, https://www.ssllabs.com/index.html
- JavaScript 中数组实用浅析
本文适用于HTML.ASP 中的 JavaScript 脚本代码.代码以 HTML 中的 JS 为例,如果在 ASP 中,请将 document.write 改为 Response.Write 即可. ...
- QT Creater + vs2010 发布程序
这几天帮同学写了个简单的gui应用,用的qt5.0.2_msvc2010.写的程序需要在一台没有装过vs和qt的机子上运行. 在release下编译运行通过后,把相应的依赖dll加入到exe相同的文件 ...
- 数据导入读取read.table函数详解,如何读取不规则的数据(fill=T)
函数 read.table 是读取矩形格子状数据最为便利的方式.因为实际可能遇到的情况比较多,所以预设了一些函数.这些函数调用了 read.table 但改变了它的一些默认参数. 注意,read.ta ...
- Ext4,Ext3的特点和区别(转)
Linux kernel 自 2.6.28 开始正式支持新的文件系统 Ext4. Ext4 是 Ext3 的改进版,修改了 Ext3 中部分重要的数据结构,而不仅仅像 Ext3 对 Ext2 那样,只 ...
- pgpgin|pgpgout|pswpin|pswpout意义与差异
引用来自: http://ssms.cs2c.com.cn/otrs/pc.pl?Action=PublicFAQZoom;ItemID=11741 文章主要意思是: 1. page in/out操作 ...
- Ios开发之sqlite
Sqlite是ios数据存储的一个重要手段,今天我们就一块来看一下,怎样使用sqlite将数据存储到沙盒中去. 第一步:导入一个框架libsqlite3.0.dylib 选中TARGETS在Gener ...
- dubbo通信协议之对比
对dubbo的协议的学习,可以知道目前主流RPC通信大概是什么情况,本文参考dubbo官方文档 http://dubbo.io/User+Guide-zh.htm dubbo共支持如下几种通信协议: ...
- Apache Internal Server Error
当使用 Apache 作为服务器,使用 cgi 程序接收来自 web 端的访问时,出现如下错误: Internal Server Error The server encountered an int ...