C#资源释放及Dispose、Close和析构方法

备注:此文的部分观点有误,之所以仍旧保留本文,是需要在后期给出一个勘误版。正确的版本在这里“C#中标准Dispose模式的实现

一:什么是资源

在开始本文前,需要一些准备知识。首先要提出“什么是资源”。在CLR出来之后,Windows系统资源开始分为“非托管资源”和“托管资源”。

非托管资源是指:所有的Window内核对象(句柄)都是非托管资源,如对于Stream,数据库连接,GDI+的相关对象,还有Com对象等等,这些资源并不是受到CLR管理;

托管资源是指:由CLR管理分配和释放的资源,即由CLR里new出来的对象。

其次再来讲,资源的释放方式。

非托管资源:需要显式释放的,也即需要你写代码释放;

托管资源:并不需要显式释放,但是如果引用类型本身含有非托管资源,则需要进行现实释放;

二:显式释放的C#实现

显式释放的C#实现,由C#语法支持的有:

1:实现IDisposable接口的Dispose方法;

2:析构方法(终结器);

不由C#语法支持,但是约定支持的显式释放是:

3:提供显示释放方法,比如常用的Close方法;

三:Dispose、Close和析构方法异同点

但是,还需要区分这3种方式的异同点。首先,你无法调用析构方法。析构方法是由垃圾回收机制进行调用的。换句话来说,就是你不知道析构方法被调用的时机。严格意义上来说,它只是作为资源释放的一个补救措施。

资源释放的一个正确的措施是为类型实现IDisposable接口的Dispose。当你需要释放类型的资源的时候,应该显示的调用Dipose方法。当然,这里还有一个C#的语法糖,就是使用using程序块,在离开using程序块的时候,CLR会自动调用类型所创建对象的Dipose方法。

可能有人会问道,既然可以通过Dispose方法的方式来进行资源的释放,为什么有些类型还需要提供一个Close方法。这里面的区别,或者说约定在于,如果你仔细观察这些类型:他们基本都只公开了Close方法,他们都实现了IDisposable,但都隐藏了Dispose方法。以Socket这个类为例,它:

1:提供public void Close()

public void Close() { //…. ((IDisposable)this).Dispose(); //…. }

2:提供显式void IDisposable.Dispose()

void IDisposable.Dispose() { this.Dispose(true); GC.SuppressFinalize(this); }

3:提供protected virtual void Dispose(bool disposing)。真正的资源释放的代码放在这里。

所以理论上来将,提供Close方法最终还是使用的Dispose方法,之所以这么做,是因为这些类型出于显式实现IDisposable的因素,在调用这些Dispose方法的时候,必须完成一次转型,如:

((IDisposable)new A()).Dispose();

为了避免转型,同时也为了避免不熟悉C#语法的开发人员更直观的释放资源,提供了Close方法。

在上文的例子中,你可能已经注意到IDisposable.Dispose这个方法中,包含一句: 

GC.SuppressFinalize(this); 

这是告诉CLR,在进行垃圾回收的时候,不用再继续调用析构方法(终结器)了。是的,因为你已经手动释放资源了。这也从另一个方面验证了析构方法只是作为资源释放的补救机制。因为假设你忘记Close或者Dispose了,CLR会在垃圾回收的时候为你做这件事。查看Socket的析构函数,你会很好的理解这一点。

~Socket() { this.Dispose(false); }

是的,析构方法调用的也是Dispose。

备注1:本文带来几个争论

1:托管资源本身是否需要显式释放。答案显然是:不需要;

2:如果引用类型对象不再需要,是否需要显式=null;答案是:即使不这样做,GC也会进行垃圾回收。

3:将托管资源分为引用类型资源和值类型资源这种分类方法是有问题的,或者说是错误的。正确的分类法应该是栈资源和堆资源。线程栈中存放的是方法的实参和方法内部的局部变量。堆上存放的是类型对象本身及对象的两个额外成员:类型对象指针和同步块索引。

4:Dispose方法本身是用来让你放置资源清理代码的。显然,一个空方法并不代表清理工作本身,真正执行清理工作的是你具体的代码。

备注2:推荐Dipose模式实现

如:基类

代码

class ClassShouldDisposeBase : IDisposable { public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); }
protected virtual void Dispose(bool disposing) { if (disposing) { //执行基本的清理代码 } }
~ClassShouldDisposeBase() { this.Dispose(false); }
}

子类:

代码

class ClassShouldDispose: ClassShouldDisposeBase { protected virtual void Dispose(bool disposing) { if (disposing) { // 执行子类清理代码 // 如有必要,执行base.Dispose(disposing); } else { // 如有必要,执行base.Dispose(disposing); } }
public void Close() { //调用本类或者基类的Dispose方法 //其它代码 } }

时间: 2024-11-03 17:13:10

C#资源释放及Dispose、Close和析构方法的相关文章

资源释放的问题

转 个人体悟,析构貌似有点鸡肋了,不能显示调用,不也确定调用时机,应该只是做为一种释放资源机制的存在 然后,一个实例类的资源释放应在承载它的方法结束时发生,所以做为主程序结束标志的main函数里才不能写一些冗杂的方法在里面,应把方法写在主程序外面,要用的时候调用一下这样可以省资源.而且有复用性.不知道我这样理解是否有问题.若有不对之处,还请指点,入门新人! C#中Dispose.析构函数.close的区别 本文导读:C#中析构函数 和 Dispose 都是释放资源,析构函数用于 隐式释放资源,D

.net 资源释放(托管资源和非托管资源)

1.托管资源 像int.float.DateTime等都是托管资源:net中80%的资源都是托管资源: 托管资源的回收通过GC(垃圾回收器)自动释放分配给该对象的内存,但无法预测进行垃圾回收的时间,我们无法控制系统在什么时间回收资源. 2.非托管资源 像ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Object,Odbc

泛型的资源释放

C#泛型是一种高复用性.安全和高效的技术,通过类型参数可以将参数的声明.实现推迟到客户代码中.但是这种延迟却降低了类型参数在泛型定义中的可操作性.例如资源释放. public interface IWorker { void Operate(); } public class GenericWorker<T> where T : IWorker , new() { public void GetResult() { T worker=new T(); worker.Operate(); } }

深刻理解C#中资源释放

今天我的一个朋友看到我写的那篇<C#中用AJAX验证用户登录>时,给我指出了点小毛 病.就是在用户登录时,如果用户登录失败,在下面这段代码中,都会new出来一个User对象,如果连续登录失败多次,就会生成多个User对象,而它们 在登录失败后已经无用了,依然占据着内存,就算是C#有垃圾回收机制,但不确定什么时候对这些对象进行回收.然后去网上找了一篇C#资源释放的文章,讲的很透彻,和大家分享一下. using System;using System.Collections.Generic;usi

Winform开发之窗体显示、关闭与资源释放

Winform的窗体涉及到一般窗体(单文档窗体).MDI窗体.窗体之间的关系等,那么如果调用打开新窗体.如何关闭窗体.窗体资源的释放等都关系到软件运行的效率,本文一一介绍 1.窗体的显示 从一个窗体打开另一个窗体,基本会用到两种方法,即Show()和ShowDialog(),前者是一般的窗体,后者是模态窗体. 两种调用是由些许的不同 (1)在调用Form.Show方法后,Show方法后面的代码会立即执行 (2)在调用Form.ShowDialog方法后,写在其后面的程序会在模态窗口关闭后再执行.

opencv资源释放问题

初学opencv,资源释放问题困扰了好久.感觉小有体会,仅供菜鸟参考. 资源要不要释放主要看是否真正在内存开辟空间.简单点说就是有Creat开辟空间,才有Release的释放空间.当然实际情况要更复杂,比如clone()等深复制.总之还是要看是否真正在内存开辟空间. 具体的参考一下以下3篇文章吧 http://blog.csdn.net/scudz/article/details/8083866 http://blog.csdn.net/liulina603/article/details/84

cocos2dx loading界面 预加载资源 与 资源释放

预加载图片: 1.CCTextureCache::sharedTextureCache()->addImage("icon.png"); 2.CCTextureCache::sharedTextureCache()->addImageAsync("icon.png",this,callfuncO_selector(MainLayerLoading::loadingCallBack)); 使用加载的缓存图片: CCSprite* sp =CCSprite:

oracle for update锁表资源释放之kill -9和alter system kill session &#39;sid,serial#&#39;;

通过for update锁表,通过操作系统方式和oracle方式终止进程方式 --查询需要终止进程的情况,包括操作系统进程 select proc.sPID, sess.sid, sess.serial#, lo.oracle_username, lo.os_user_name, ao.object_name, lo.locked_mode from v$locked_object lo, dba_objects ao, v$session sess,v$process proc where a

JDBC的资源释放

1.1.1?JDBC资源释放 JDBC程序执行结束后,将与数据库进行交互的对象释放掉,通常是ResultSet,Statement,Connection. 这几个对象中尤其是Connection对象是非常稀有的.这个对象一定要做到尽量晚创建,尽早释放掉. l 将资源释放的代码写入到finally的代码块中. l 资源释放的代码应该写的标准: if(rs !=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(