++中那样内存管理工作的复杂性。虽然不必手工管理内存,但如果要编写高效的代码,就仍需理解后台发生的事情。
一面运行没有错误,可总想知道这个dispose方法到底做了些什么.既然是释放资源,那么类被释放后应该就被销毁,它的引用应该是不存在的,于是本人的测试代码如下:
Code
try
{
if (_foo == null)
{
//对象调用Dispose()后应该运行到此外
Response.Write("资源已经释放啦!");
}
else
{
Response.Write(_foo.GetType().ToString());
//资源是否已经被释放 此时为true
bool isRelease4 = _foo.m_disposed;
bool isAdded2 = _foo.AddUser();
}
}
catch (Exception ex)
{
Response.Write("ERR");
}
本想应该会运行Response.Write("资源已经释放啦!"),可是结果相反,它的引用依然存在.这让我不解,后来得到园友jyk的指点,他让我试下,.net下实现了dispose方法的类,我就用Stream试了下,测试结果好下:
Code
Stream _s = this.FileUpload1.PostedFile.InputStream;
//客户端文件大小 为了判断对象是否被销毁
long orgLength = _s.Length;
_s.Dispose();
try
{
if (_s == null)
{
Response.Write("资源已经释放啦!");
}
else
{
Response.Write(_s.GetType().ToString());
//客户端文件大小 此处为释放资源后
//运行结果表明,此时的文件流的大小是0
//说明资源已经成功释放
long _length= _s.Length;
}
}
catch (Exception ex)
{
Response.Write("ERR");
}
运行结果我们可以非常清楚的看出,Stream资源已经被释放,因为两次访问Stream的大小,发现在dispose后的大小为零.这就好像是第一次初始化的结果.但Stream属于非托管资源,如果是托管资源呢?在Foo的测试代码中发现,释放前后的变量(m_disposed,调用Dispose前为false,调用后为true,而且还可以调用类的方法)发生了变化,并不是我想象当中的初始化.这是让我一直不解的地方.
后来在资料书上看,发现IDisposable接口是专门针对未托管资源而设计的.它在托管资源上没有特别大的帮助.最终的资源回收工作还得要GC.我们看下托管资源和非托管资源在内存上的分配情况.
/*非托管资源一般都是放在堆栈上,而托管资源都是存储在堆上.*/ 非常感谢 Angel Lucifer的指教,本人见笑了 特此删除:
值类型与引用类型在内存分配上的分别:
值类型存储在堆栈中,堆栈的工作原理就是先进后出.它在释放资源的顺序上与定义变量时分配内存的顺序相反.值变量一旦出了作用域就会从堆栈中删除对象.
引用类型则存储在堆中.,当new一个类时,此时就会为对象分配内存存入托管堆中,它可以在方法退出很长的时间后仍然可以使用.我以一句常用的实例类的语句来说明下.
classA a=new classA();
这句非常平常的语句其实可以分成两部分来看:
第一:classA a;声明一个classA的引用a,在堆栈上给这个引用分配存储空间.它只是个引用,并不是真正的对象.它包含存储对象的地址.
第二:a=new classA();分配堆上的内存,以存储真正的对象.然后修改a的值为新对象的内存地址.
当引用出了作用域后,就会从堆栈上删除引用,但引用对象的数据仍然存储在托管堆中,一直到程序停止,或者是GC删除.
所在这点就可以解释我上面写的Foo类在调用Dispose方法后,程序仍然可以访问对象的原因了.
/*我认为堆是否就有像asp.net中的缓存功能,它可以将对象缓存起来,对象只要创建一次就可以在一定的有限时间内存在.*/
非常感谢 Angel Lucifer的指教 特此更正如下:
这种情况完全是因为GC回收操作的不可预测性导致的。GC Heap上的对象生存期完全看GC是否要回收它而决定。此外,值类型完全没必要实现 IDisposable 接口。
总结:
如果你的类中没有用非托管资源,或者是非常大的实例(象 GIS 中的Geometry), 就没有太大的必要实现这个