C#中析构函数、Dispose、Close方法的区别
一、Close与Dispose这两种方法的区别
调用完了对象的Close方法后,此对象有可能被重新进行使用;而Dispose方法来说,此对象所占有的资源需要被标记为无用了,也就是此对象要被销毁,不能再被使用。例如常见.Net类库中的SqlConnection这个类,当调用完Close方法后,可以通过Open重新打开一个数据库连接,当彻底不用这个对象了就可以调用Dispose方法来标记此对象无用,等待GC回收。
二、三者的区别如图
析构函数
Dispose方法
Close方法
意义
销毁对象
销毁对象
关闭对象资源
调用方式
不能被显示调用,在GC回收是被调用
需要显示调用或者通过using语句
需要显示调用
调用时机
不确定
确定,在显示调用或者离开using程序块
确定,在显示调用时
三、析构函数和Dispose的说明
Dispose需要实现IDisposable接口。
Dispose由开发人员代码调用,而析构函数由GC自动调用。
Dispose方法应释放所有托管和非托管资源。而析构函数只应释放非托管资源。因为析构函数由GC来判断调用,当GC判断某个对象不再需要的时候,则调用其析构方法,这时候该对象中可能还包含有其他有用的托管资源。
Dispose方法结尾处加上代码“GC.SuppressFinalize(this);”,即告诉GC不需要再调用该对象的析构方法,否则,GC仍会在判断该对象不再有用后调用其析构方法,虽然程序不会出错,但影响系统性能。
析构函数和Dispose释放的资源应该相同,这样即使类使用者在没有调用Dispose的情况下,资源也会在Finalize中得到释放。
Finalize不应为public。
通过系统GC频繁的调用析构方法来释放资源会降低系统性能,所以推荐显示调用Dispose方法。
有Dispose方法存在时,应该调用它,因为Finalize释放资源通常是很慢的。
四、Close函数的说明
Close这个方法在不同的类中有不同的含义,并没有任何规定要求Close具有特殊的含义,也就是说Close并不一定要释放资源,您也可以让Close方法表示“关门”。 不过,由于Close有“关”的意思,通常也把Close拿来释放资源,这也是允许的。比如文件操作中,用Close释放对象似乎比Dispose含义更准确,于是在设计类时,可以将Close设为public,将Dispose设为protected,然后由Close调用Dispose。也就是说Close表示什么意思,它会不会释放资源,完全由类设计者决定。网上说“Close调用Dispose”这种方法是很片面的。在SqlConnection中Close只是表示关闭数据库连接,并没有释放SqlConnection这个对象资源。 根据经验,Close和Dispose同时存在的情况下(均为public),Close并不表示释放资源,因为通常情况下,类设计者不应该使用两个public方法来释放相同的资源。
五、析构函数和Dispose方法实例
publicclassBaseResource:IDisposable { //前面我们说了析构函数实际上是重写了System.Object中的虚方法Finalize,默认情况下,一个类是没有析构函数的,也就是说,对象被垃圾回收时不会被调用Finalize方法 ~BaseResource() { //为了保持代码的可读性性和可维护性,千万不要在这里写释放非托管资源的代码 //必须以Dispose(false)方式调用,以false告诉Dispose(booldisposing)函数是从垃圾回收器在调用Finalize时调用的 Dispose(false); } //无法被客户直接调用 //如果disposing是true,那么这个方法是被客户直接调用的,那么托管的,和非托管的资源都可以释放 //如果disposing是false,那么函数是从垃圾回收器在调用Finalize时调用的,此时不应当引用其他托管对象所以,只能释放非托管资源 protectedvirtualvoidDispose(booldisposing) { //那么这个方法是被客户直接调用的,那么托管的,和非托管的资源都可以释放 if(disposing) { //释放托管资源 OtherManagedObject.Dispose(); } //释放非托管资源 DoUnManagedObjectDispose(); //那么这个方法是被客户直接调用的,告诉垃圾回收器从Finalization队列中清除自己,从而阻止垃圾回收器调用Finalize方法. if(disposing) GC.SuppressFinalize(this); } //可以被客户直接调用 publicvoidDispose() { //必须以Dispose(true)方式调用,以true告诉Dispose(booldisposing)函数是被客户直接调用的 Dispose(true); } }
上面的范例达到的目的:
1/如果客户没有调用Dispose(),未能及时释放托管和非托管资源,那么在垃圾回收时,还有机会执行Finalize(),释放非托管资源,但是造成了非托管资源的未及时释放的空闲浪费
2/如果客户调用了Dispose(),就能及时释放了托管和非托管资源,那么该对象被垃圾回收时,不回执行Finalize(),提高了非托管资源的使用效率并提升了系统性能
最后:
如果您的类中使用了非托管资源,则要考虑提供Close方法,和Open方法。并在您的Dispose方法中先调用Close方法。
在使用已经有类时,如SqlConnection。如果暂时不用这个连接,可以考虑用Close()方法。如果不用了就考虑调用Dispose()方法。