值类型离开作用域就会被销毁,内存会被回收。
创建对象过程
Square mySquare = new Square ();
new 表面上是单步操作,但实际要分两步走
1、首先,new操作从堆中分配原始内存。这个阶段无法进行任何干预。
2、然后,new操作将原始内存转换成对象;它必须初始化对象。可用构造器控制这一阶段。
销毁对象过程
1、CLR(Common Language Runtime)执行清理工作,可以写一个析构器加以控制。
2、CLR将对象占用的内存归还给堆,解除对象内存的分配。对这个阶段你没有控制权。
销毁对象并将内存归还给堆的过程称为垃圾回收。
C#完全由CLR控制何时销毁对象。
编写析构器(析构器只有在对象被垃圾回收时才运行)
使用析构器,可以在对象被垃圾回收时执行必要的清理工作。析构器肯定会运行,只是不保证在什么时间运行。
和构造器相似,析构器也是一个特殊的方法,只是CLR会在对象的所有引用都消失之后调用它。析构器的语法是先写一个~符号,然后添加类名。
class FileProcessor{
FileStream file = null;
public FileProcessor(string fileName){
this.file = File.OpenRead(fileName);//打开文件来读取
}
~FileProcessor() //析构器
{
this.file.Close();//关闭文件
}
}
析构器存在下面这些非常重要的不足:
1、析构器只适合引用类型。值类型(例如结构)中不能声明析构器。
2、不能为析构器指定访问的修饰符(例如public)。这是由于永远不在自己的代码中调用析构器——总是由垃圾回收器(CLR的一部分)帮你调用。
3、析构器不能获取任何参数。
编译器内部自动将析构器转换成对Object.Finalize方法的一个重写版本的调用。例如:
编译器将以下析构器:
class FileProcessor{
~FileProcessor() //析构器
{
//这里放你的代码
}
}
转换成以下形式:
class FileProcessor{
protected override void Finalize()
{
try{//这里放你的代码}
finally{base.Finalize}
}
}
注意:只有编译器才能进行这个转换。你不能自己重写Finalize,也不能自己调用Finalize。
为什么要使用垃圾回收器
在C#中,你永远不能亲自销毁对象。没有任何语法支持这个操作。相对,CLR在它认为合适的时间帮你做这件事情。
对象的生存期管理是相当复杂的一件事情,这正是C#的设计者决定禁止由你销毁对象的原因。如果程序员负责销毁对象,迟早会遇到以下情况之一:
1、忘记销毁对象。
2、试图销毁对象,造成一个或多个变量容纳对象已销毁的对象的引用,即所谓的虚悬引用。
3、试图多次销毁同一对象。
在C#这种将可靠性和安全性摆在首要位置的语言中,这些问题当然是不能接受的。取而代之的是,必须由垃圾回收器负责销毁对象。垃圾回收器能做出以下几点担保:
1、每个对象都会被销毁,它的析构器会运行。
2、每个对象只被销毁一次。
3、每个对象只有在它不可抵达时(不再有该对象的任何引用)才会被销毁。
慎用析构器:写包含析构器的类,会使代码和垃圾回收过程变复杂。此外i,还会影响程序的运行速度。如果程序不包含任何析构器,垃圾回收器就不需要将不可抵达的对象放到freachable队列并对它们进行“终结”(也就是不需要运行析构器)。显然,一件事情做和不做相比,不做会快一些。所以,除非确有必要,否则请尽量避免使用析构器。例如,可以改为使用using语句。
资源管理
有时在析构器中释放资源并不明智。有的资源过于宝贵,用完后应马上释放,而不是等待垃圾回收器在某个不确定的时间释放。内存、数据库连接和文件句柄等稀缺资源应尽快释放。这时唯一的选择就是亲自释放资源。这是通过自己写的资源管理方法来实现的。可显示调用类的资源清理方法,从而控制释放资源的时机。
using语句提供了一个脉络清晰的机制来控制资源的生存周期。可以创建一个对象,这个对象会在using语句块结束时销毁。
using语句的语法如下:
using(类型 变量 = 初始化)
{
语句块
}
下面是确保代码总是在TextReader上调用Close的最佳方式:
using (TextReader reader = new StreamReader(filename))
{
string line;
while((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
这个using语句完全等价于一下形式:
{
TextReader reader = new StreamReader(filename);
try{
string line;
while((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
finally
{
if(reader != null)
{
((IDisposable)reader).Dispose();
}
}
}
using语句声明的变量的类型必须实现IDisposable接口。IDisposable接口在System命名空间中,只包含一个名为Dispose的方法。
namespace System
{
interface IDisposable
{
void Dispose();
}
}
Dispose方法的作用是清理对象使用的任何资源。