.Net中,垃圾回收器负责回收你创建的引用类型的对象,但是回收时间并不能准确估计出来,所以这称之为非确定销毁。值类型自动释放,所以不在本文讨论之中。
但是有些稀缺资源,比如文件句柄、数据库连接等,就需要尽快释放。如何做到呢。最简单的方法就是调用GC.Collect ()强迫垃圾回收器工作。但是这种方法会降低性能,除非迫不得已。
那么有没有更好的办法?
“析构函数”
“析构函数”与c++析构函数的区别
“析构函数”怎么样?比如下面的例子:
class Class1
{
public Class1()
{
Console.WriteLine("constructor");
}
~Class1()
{
Console.WriteLine("destructor");
}
}
其实c#中的“析构函数”并不是c++程序员心中的析构函数,在c++中,跳出对象所生存的堆栈时或者调用delete的时候,析构函数就会被调用。c#提供了一种语法相近,但是语义完全不同的“析构函数”.实际上,该函数只是重载了System.Object类的Finalize函数。
System.Object是c#中所有的类型的基类型,由于是隐式派生,所以不需要在派生列表中指定它。Finalize函数的访问权限是protected,只能被自己和派生类使用,实际上在垃圾回收器销毁对象时,将会给该对象一次调用Finalize的机会,这样我们可以将自己需要的逻辑放在“析构函数”里面。如果对象构造期间出现异常,则该方法也会被调用。
但是,经过以上分析,可以看出,“析构函数”并不能让我们确定销毁某对象。
“析构函数”的危害
顺便说一下,C#代码应该总是使用“析构函数”,而不是手动重载Finalize函数,因为“析构函数”会自动将内部代码放到try块内,并且在finnally块内部调用base.Finalize方法。
“析构函数”正常情况下是不建议创建的,因为它会提升该类以及被引用的其他类的代龄,这会导致垃圾回收器过早的运行,从而影响性能,同时每次垃圾回收器工作时都要额外的调用“析构函数”,这显然也是对性能有影响的,如无必要,就不要为自己的类创建“析构函数”。
更严重的情况,如果A类有“析构函数”,B类有“析构函数”,A有一个B类的变量,当垃圾回收器工作时,A和B的“析构函数”调用顺序却没有得到保证。这样会导致错误,这比性能问题更严重。
Oh,my!尽量不要在“析构函数”里面访问托管成员变量,除非销毁非托管资源。但是通常非托管资源都需要确定销毁,显然这时不能用“析构函数”解决问题。
GC.SuppressFinalize ( )将阻止Finalize被调用。
IDisposable接口
不如提供一个方法,该方法能够将需要提前释放的资源释放掉,然后该对象可以到垃圾回收器工作的时候再释放。这就是Dispose方法。
class Class1:IDisposable
{
public Class1()
{
GC.SuppressFinalize(this);
Console.WriteLine("constructor");
}
~Class1()
{
Console.WriteLine("destructor");
}
public void Dispose()
{
GC.SuppressFinalize(this);
Console.WriteLine("Dispose");
}
}
GC.SuppressFinalize(this);这句话告诉垃圾管理器,从现在开始,不准调用我的“析构函数,当然不能调,因为我的Dispose后面还要执行清理资源的代码,垃圾回收器如果抢在前面将对象回收,就会出错。如果类没有实现“析构函数”,当然就不需要这句话了。
Close方法
为了适应大多数程序员的习惯,可以添加一个Close方法,其实内部只是调用了Dispose而已。不高兴的话,也可以懒得实现它。
Using 方法
为了减少编写异常处理代码,using语法可以保证无论是否发生异常,都为调用Dispose方法或者Close方法,前提是对象支持Idisposable接口。所以这是一个很简洁的语法。
126家具材料网