一:什么是资源
在开始本文前,需要一些准备知识。首先要提出“什么是资源”。在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方法
//其它代码
}
}
本文基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名http://www.cnblogs.com/luminji(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言。、
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Dispose()
在向大家详细介绍C#调用Dispose()方法之前,首先让大家了解下Dispose()方法,然后全面介绍C#调用Dispose()方法。
我们已经知道了处置那些占用非受控(unmanaged)资源的对象的重要性,现在应该编写资源管理代码来处置那些包含非内存资源的类型了。整个.NET 框架组件都使用一个标准的模式来处理非内存资源。使用你建立的类型的用户也希望你遵循这个标准的模式。标准的处理模式的思想是这样的:当客户端记得的时候使用IDisposable接口释放你的非受控资源,当客户端忘记的时候防护性地使用终结器(finalizer)。它与垃圾收集器(Garbage Collector)一起工作,确保只在必要的时候该对象才受到与终结器相关的性能影响。这是处理非受控资源的一条很好的途径,因此我们应该彻底地认识它。
类层次体系中的根基类(root base class)必须实现IDisposable接口以释放资源。这个类型还必须添加一个作为防御机制的终结器。所有这些程序都把释放资源的工作委托给一个虚拟的方法,衍生的类可以根据自己的资源管理需求来重载该方法。只要衍生的类必须释放自己的资源,并且它必须调用该函数的基类版本的时候,它才需要重载这个虚拟方法。
开始的时候,如果你的类使用了非内存资源,它就必须含有一个终结器。你不能依赖客户端总是C#调用Dispose()方法。因为当它们忘记这样做的时候,你就面临资源泄漏的问题。没有调用Dispose是它们的问题,但是你却有过失。用于保证非内存资源被正确地释放的唯一途径是建立终结器。
当垃圾收集器运行的时候,它立即从内存中删除所有不带终结器的垃圾对象。所有带有终结器的对象仍然存在于内存中。这些对象都被添加到终结队列,垃圾收集器引发一个新线程,周期性地在这些对象上运行终结器。在这些终结程序线程完成自己的工作之后,就可以从内存中删除垃圾对象了。需要终结的对象在内存中停留的时间比没有终结器的对象停留的时间长很多。但是你别无选择。如果要使程序有防护性,在类型包含非受控资源的时候,你必须编写一个终结器。但是也不用担心性能问题。下一步确保了客户端避免与终结相关的性能开销。
实现IDisposable接口是一种标准的途径源码天空,它通知用户和运行时系统持有资源的对象必须及时地释放。IDisposable接口仅仅包含一个方法:
public interface IDisposable { void Dispose( ); } 你对C#调用Dispose()方法的实现(implementation)负责下面四个事务:
1、释放所有的非受控资源。
2、释放所有的受控资源(包括未解开事件)。
3、设置标志表明该对象已经被处理过了。你必须在自己的公共方法中检查这种状态标志并抛出ObjectDisposed异常(如果某个对象被处理过之后再次被调用的话)。
4、禁止终结操作(finalization)。你调用GC.SuppressFinalize(this)来完成这种事务。
通过实现IDisposable接口你完成了两个事务:你为客户端及时地释放自己持有的所有受控资源提供了机制;你为客户端提供了一种释放非受控资源的标准途径。这是一个很大的进步。当你在类型中实现Idisposable接口的时候,客户端可以避免终结操作的开销,你的类就成为.NET世界中的"良民"了。
详细请参考:http://www.codesky.net/article/200908/127376.html