熟悉C#的程序员都知道,在C#类中,有一种函数类似于C++类析构函数,它们的表达方式也一样,都是 ~。没错,它就是C#中的终结器。
乍看之下,C#终结器和C++析构函数好像一样,相同的表达方式,同样一个类只能有一个,同样是在类对象被销毁的时候由系统调用。
但是由于C#垃圾回收机制的存在,C#终结器并不能所分配的类对象超出其生命周期的时候及时被调用,它只会被垃圾回收器标记,并在垃圾回收器运行的时候调用。换言之,终结器被调用的时机不定,我们只知道终结器要被调用,但是什么时候被调用就只有垃圾回收器才知道了。
正因为如此,终结器在C#中出场的次数寥寥可数,远不及析构函数在C++那边的出场率。毕竟,一个不知道什么时候会被调用的函数,在大部分情况下是没什么用处的。
不过,在某些场景下面,终结器仍然是有用的。
举个栗子
想象我们有个类,类中有个Close方法,为了确保类对象的状态正确,我们在每次使用完类对象的时候,需要调用Close方法。我们会怎么做呢?
添加using
实现Disposable接口,确保Dispose方法调用Close方法并在使用类的时候加上using语句。
public class MustCloseProperly : IDisposable
{
public MustCloseProperly()
{
//something important created
}
public void Dispose()
{
Close();
}
public void Close()
{
//close it
}
public void DoSomething()
{
//do something
}
}
public static void Main()
{
using(MustCloseProperly obj = new MustCloseProperly())
{
obj.DoSomething();
}
}
这种办法可行,也是大家常见的一种方法。但是这儿也会有一个问题,如果有些程序员大意了,没有使用using, 也没有显式调用Close,那我们有没有办法知晓呢?
第二层检查
这个时候,我们就需要利用终结器了。添加终结器,在Close中添加语句取消终结器。这样,如果终结器被触发,那么我们就可以肯定至少有类对象没有被正确的关闭。
public class MustCloseProperly
{
public void Close()
{
//close it
GC.SuppressFinalize(this);
}
~MustCloseProperly()
{
Debug.Assert(false, "you must dispose this object explicitly");
}
}
这样,当有程序员忘记了使用using并且也没有显式调用Close的时候,程序或迟或早,会以断言的形式提醒用户。当然,这取决于终结器何时调用,至于终结器何时调用,这又是另外一个话题,涉及到内存的使用率和垃圾回收器的运行机制,有机会我们再说。
但是这个方法不是万无一失的,如果一个程序运行的特别快,使用的内存也特别少,那么很有可能垃圾回收器还没有来得及运行程序就顺利结束了,错误也就被掩盖了。所以这个方法不是100%保险。如果小伙伴有更好更保险的检测方法,请留言告诉我哟!当然最好的办法是坚持对Dispose对象使用using。