zoukankan      html  css  js  c++  java
  • C#析构器的一个Bug

    【2003/12/03】

    http://www.lijianzhong.com/Index.asp
    这是很早发现的C#语言的一个“bug”,总以为MS很快就会修复,可是从1.0等到1.1,再从1.1等到1.2(前面两个是.NET框架的Realse版,后面一个是Alpha版本的Longhorn操作系统中自带的.NET框架版本;1.2版的.NET框架中包含的是支持C# 2.0 Spec的编译器),一直没有改进。难道这就是C#设计者们对C#本来的设计?我说服不了自己。如果各位朋友有什么见解,希望能够就这个问题做深入的交流。问题如下:

    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#编译器并没有对此给予警告或者禁止,C#规范也没有指出定义这样的Finalize方法就是在定义一个析构器——实际上也不是,只是上述代码的表现如此——甚至还有这样一句诱人犯错的话:The compiler behaves as if this method(Finalize), and overrides of it, do not exist at all。分析IL代码可以看出,Parent中定义的“普通”的Finalize方法实际上“欺骗”了它的子类。它的子类只关心其父类是否定义了Finalize(当然签名要为上述形式)方法,它并不关心那个Finalize方法是否具有“析构器”语义。

    如果上述代码的行为通过理性分析还算可以接受的话,那么下面代码的运行结果就令人眩晕了,将Parent类重新定义如下(在上面的基础上添加了一个virtual关键字):

    public class Parent:Grandpapa
    {
         protected virtual void Finalize(){ Console.WriteLine("Parent.Finalize");}
    }

    编译后运行结果如下:

    Grandpapa.~Grandpapa

    这一次从IL代码的角度也解释不清了,我怀疑CLR对于析构器的判断是否还有另外的附加条件(也许是一个特殊的元数据标记),但无论如何C#编译器呈现的行为是诡异的,因为这种结果放到哪里都是难以自圆其说的。

    说了这么多,并不是想说明C#有多么严重的缺陷,实际上在我看来C#的设计堪称完美。这只能算一个小小的“瑕疵”。最重要的问题是对于应用程序开发人员,必须了解C#语言的这个“瑕疵”,而不要被C#规范中模棱两可的语言和C#编译器的纵容而蒙蔽,否则稍有不慎就会出现非常严重且隐蔽的bug,对于服务器程序设计尤其如此。这是.NET框架带来的吗?不是,VB.NET就没有这样的问题。这是C#设计者们选择析构器语法来执行终止化操作所带来的弊病。To summarize:

    我对C#设计者的建议是

    要么彻底放弃析构器的语法(~ClassName)而直接象VB.NET中那样采用“特殊的Finalize方法”这样的概念;要么使编译器彻底禁止在一个类中定义任何的Finalize方法(后者在目前来说可能更reasonable一些)。

    而对于C#开发人员的建议则是

    不要在一个类中有定义任何Finalize方法的念头,因为那样会对你的类层次造成潜在的严重的伤害。顺便说一句,这一条建议已经收录进我规划中的一本书《C#锐利辞典》(详情见Books栏目)。

  • 相关阅读:
    几何服务,cut功能,输入要素target(修改后)内容。
    几何服务,cut功能,输入要素target(修改前)内容。
    ArcGIS Server,rest路径输入要素json 格式描述
    window对象的screen详解
    window对象的inner/outer/page/screen详解
    js的scroll详解
    js的client详解
    自己封装的操作DOM方法
    js模拟高级语言的重载
    charCode与keyCode的区别
  • 原文地址:https://www.cnblogs.com/huqingyu/p/20477.html
Copyright © 2011-2022 走看看