ninputer在“值类型的finalize不会被调用”中(http://blog.joycode.com/lijianzhong/archive/2005/01/13/42991.aspx#FeedBack)评论说“VB对Finalize管的可松?可直接重写、直接调用、允许不调用父类的Finalize,或多次调用父类的Finalize等... 与C#完全不同。
其实C#的Finalize方法看起来只比VB好一点,但还是有很隐蔽的问题。问题如下。
首先看下面的代码:
using System; public class Grandpapa { ~Grandpapa(){ Console.WriteLine("Grandpapa.~Grandpapa");} } public class Parent:Grandpapa { ~Parent(){ Console.WriteLine("Parent.~Parent");} } public class Son:Parent { ~Son(){ Console.WriteLine("Son.~Son");} } public class App { public static void Main() { Son s=new Son(); GC.Collect(); GC.WaitForPendingFinalizers(); } }
毫无疑问,这个代码的运行结果是:
Son.~Son Parent.~Parent Grandpapa.~Grandpapa
没问题。但如果重新定义Parent类别如下,会发生什么?
public class Parent:Grandpapa { protected void Finalize(){ Console.WriteLine("Parent.Finalize");} }
运行结果变成:
Son.~Son Parent.Finalize
情况有点糟糕。我在Parent中定义了一种“普通”的Finalize方法,它被它的子类Son分析器调用了?
当然,Finalize方法不是C#中的“普通”方法,而是分析器编译后有上述签名的Finalize方法。然而,C#编译器并没有禁止我们定义“普通”的Finalize,
C#规范没有指出定义这种Finalize的方法是定义一个分析器——事实上,它不是,但上述代码的性能——甚至有这样一个诱人的错误:The compiler behaves as if this method(Finalize), and overrides of it, do not exist at all。从分析IL代码可以看出,Parent中定义的“普通”Finalize方法实际上“欺骗”了它的子类。从分析IL代码可以看出,Parent中定义的“普通”Finalize方法实际上“欺骗”了它的子类。它的子类只关心它的父类是否定义了Finalize(当然,签名应该是以上形式),而不关心Finalize方法是否具有“分析器”的含义。
如果通过理性分析可以接受上述代码的行为,则以下代码的运行结果令人头晕。重新定义Parent类别如下(在上述基础上添加virtual关键字):
public class Parent:Grandpapa { protected virtual void Finalize(){ Console.WriteLine("Parent.Finalize");} }
编译后的操作结果如下:
Grandpapa.~Grandpapa