Virtualization in Super Class Constructor

2019-02-18 10:27发布

问题:

I was of the opinion that virtualization doesnt work in the super class constructor as per the design of OOP. For example, consider the following C# code.

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();
        }
    }
}

This code breaks because when the object of Descendent is made, it calls the base class constructor and we have a virtual method call in Base Class constructor which in turn calls the Derived class's method and hence, it crashes since randomValue is not intialized by that time.

A similar code works in C++ because the call to PrintRandom is not routed to the derived class since IMO, the order in C++ is something like:


1. call for base class constructor
2. Update V - Table for this class
3. call the constructor code

My Question is that firstly whether I am right that as per OOP principles, virtualization shouldn't/doesn't work in the super class constructor and secondly if I am right, then why the behavior is different in all .NET languages ( I have tested it with C#, VB.NET and MC++)

回答1:

In native C++, the program works as expected: you get the call to the base class version of the virtual function within the base class constructor. At the time of the constructor call, only the base class and its virtual functions exist, so you get the lowest-level version of the virtual function defined at the time. This does not imply that virtualization cannot be used, you just won't get the subclass versions of virtual methods in the constructors of base classes (which is why it's not recommended).

Obviously, as you can see, managed code works differently, because (iirc) the entire object is built before the constructors are called, and thus you get the subclass virtual function before the subclass constructor. This is a documented difference between the behaviors of the languages, but should be consistent across .NET languages (since they all compile to the same IL).



回答2:

This isn't a matter of OO principles in my view - it's up to the platform in question how it handles this particular conundrum. Calling a virtual method from a constructor is discouraged for precisely this reason, however - if you're going to do it, you need to document very explicitly that you're going to call it, so that any class overriding it knows what to expect.

Java takes the same approach as .NET except that in C# any instance variable initializers are executed before the base constructor call is made. This means that in your particular example, you can fix the code by initializing random at the point of declaration. In Java that wouldn't help.

As for why MC++ doesn't work this way, I don't know - I suggest you compare the IL generated. My guess is that it explicitly makes a non-virtual method call.

EDIT: I suspect I misread the question - which way does MC++ work? If it works the way C# works, that's a good thing IMO, providing a consistent view across the .NET platform.



回答3:

I'd suggest using FxCop on your code. I've worked with many folks who dismiss the items raised by this tool as inconsequential, but, if your code contains lots of minor issues (such as yours), then the chances of being bitten by one or more are that much higher.

ReSharper's code analysis will pick up this particular issue too.



标签: c# .net c++ oop