代码之家  ›  专栏  ›  技术社区  ›  Aamir

超类构造函数中的虚拟化

  •  4
  • Aamir  · 技术社区  · 16 年前

    我认为按照OOP的设计,虚拟化在超类构造函数中不起作用。例如,考虑下面的C代码。

    using System;
    namespace Problem
    {
        public class BaseClass 
        {
            public BaseClass() 
            {
                Console.WriteLine("Hello, World!");
                this.PrintRandom();
            }
            public virtual void PrintRandom() 
            {
                Console.WriteLine("0");
            }
        }
    
        public class Descendent : BaseClass 
        {
            private Random randomValue;
            public Descendent() 
            {
                Console.WriteLine("Bonjour, Monde!");
                randomValue = new Random();
            }
            public override void PrintRandom() 
            {
                Console.WriteLine(randomValue.NextDouble().ToString());
            }
    
            public static void Main() 
            {
                Descendent obj = new Descendent();
                obj.PrintRandom();
                Console.ReadLine();
            }
        }
    }
    

    这段代码之所以会中断,是因为当生成子对象时,它会调用基类构造函数,而我们在基类构造函数中有一个虚拟方法调用,而基类构造函数又会调用派生类的方法,因此它会崩溃,因为此时randomValue没有初始化。

    类似的代码在C++中工作,因为调用了PrimtRANDOM,因为IMO没有路由到派生类,C++中的顺序类似于:


    1.调用基类构造函数

    3.调用构造函数代码

    我的问题是,首先,根据OOP原则,虚拟化不应该/不应该在超类构造函数中工作,我是否正确;其次,如果我正确,那么为什么在所有.NET语言中的行为都不同(我已经用C#、VB.NET和MC++对其进行了测试)

    3 回复  |  直到 14 年前
        1
  •  4
  •   Nick    16 年前

    在本机C++中,程序按照预期工作:您可以调用基类构造函数中的虚函数的基类版本。在构造函数调用时,只有基类及其虚拟函数存在,因此您可以获得当时定义的虚拟函数的最低级别版本。这并不意味着不能使用虚拟化,只是在基类的构造函数中无法获得虚拟方法的子类版本(这就是为什么不建议使用虚拟化)。

    显然,正如您所看到的,托管代码的工作方式不同,因为(iirc)整个对象是在调用构造函数之前构建的,因此您在子类构造函数之前获得子类虚函数。这是两种语言行为之间的差异,但在.NET语言中应该是一致的(因为它们都编译为相同的IL)。

        2
  •  3
  •   Jon Skeet    16 年前

    在我看来,这不是OO原则的问题——这取决于所讨论的平台如何处理这一特殊难题。然而,正是由于这个原因,不鼓励从构造函数调用虚拟方法——如果要这样做,需要编写文档 非常

    Java采用与.NET相同的方法 除了 在C#中,任何实例变量初始值设定项都是在进行基本构造函数调用之前执行的。这意味着在您的特定示例中,您可以通过初始化来修复代码 random 在申报时。在Java中,这没有帮助。

    至于为什么++ 这样做,我不知道-我建议您比较生成的IL。我的猜测是,它显式地进行非虚拟方法调用。

    编辑:我怀疑我误读了这个问题-MC++以哪种方式工作?如果它像C#那样工作,在我看来这是一件好事,可以在整个.NET平台上提供一致的视图。

        3
  •  0
  •   Steve Dunn supercat    16 年前

    我建议在代码中使用FxCop。我和许多人一起工作过,他们认为这个工具提出的问题无关紧要,但是,如果你的代码包含很多小问题(比如你的),那么被一个或多个问题咬伤的可能性就要大得多。

    ReSharper的代码分析也会发现这个特殊的问题。