Erik_JI
Erik_JI

Reputation: 305

Why are derived class fields initialized before the base class fields

I know that the following is the order of data members initialization and constructor call when creating a derived class object.

  1. Derived static fields
  2. Derived static constructor
  3. Derived instance fields
  4. Base static fields
  5. Base static constructor
  6. Base instance fields
  7. Base instance constructor
  8. Derived instance constructor

Everything here seems pretty logical except for ONE thing:
I can not understand why is that that derived class fields get initialized before base class fields? Honestly I can not find any valid and logical explanation for that after scouring the internet and giving it a lot of thought.

Upvotes: 2

Views: 822

Answers (3)

Ivan Petrov
Ivan Petrov

Reputation: 4802

I can not understand why is that that derived class fields get initialized before base class fields? Honestly I can not find any valid and logical explanation for that after scouring the internet and giving it a lot of thought.

The use of derived class fields from a virtual method that is invoked in the constructor of the base class is a compelling argument.

But I really think that the "real reason" is that it allows for saving ourselves unnecessary calls to base class constructors when derived fields' initialization can throw exceptions.

Example:

class A {

}

class B : A {
    int dummy = ExceptionThrowingInitializationLogic();

    static int ExceptionThrowingInitializationLogic() {
        throw new Exception("Stop construction!");
    }
}

If it had been the other way around we would have wasted a lot of calls to base classes when we could have thrown an exception immediately. Something like early returning.

Upvotes: 0

Wouter
Wouter

Reputation: 2958

Logically, you might want to use the initialized value in the constructor.

See:

public class Base
{
    private int x = 3;

    public Base()
    {
        Print();
    }

    protected virtual void Print()
    {
        Console.WriteLine(x);
    }
}

public class Derived : Base
{
    private int x = 5;

    protected override void Print()
    {
        base.Print();
        Console.WriteLine(x);
    }
}

Note: this is exactly why you shouldn't call virtual methods from the constructor because your object might not be fully initialized.

Upvotes: 1

Ibrennan208
Ibrennan208

Reputation: 1404

Consider the following class structures:

Animal:

public class Animal
{
    public string Name;
    public Animal(string name)
    {
        Name = name;
    }
}

Dog (which is derived from Animal):

public class Dog : Animal
{
    static string DogName = "Dog";

    public Dog() : base(DogName)
    {
                
    }
}

And Cat (which is also derived from Animal):

public class Cat : Animal
{
    public Cat() : base(nameof(Cat))
    {

    }
}       

If we wanted to instantiate a copy of Dog or Cat, they will both ultimately call the Animal constructor. The Animal constructor requires a string, name, be passed in as a parameter. In order for the name parameter to be passed in from either the Cat or Dog it will need to be defined somewhere. So it would have to be defined before calling the Animal constructor, but the compiler wouldn't know to do this until already trying to initialize the derived class. So ultimately, it would need to initialize at least some of the values in the derived class in order to use them to call the constructor of the base class.

For Dog, it will initialize the static DogName in order to pass it into the constructor of the base class, while for Cat, the compiler will evaluate the nameof(Cat) to pass in to the Animal constructor.


To try to capture why it would make sense to initialize some of these values, let's imagine we are the compiler trying to create a new Dog (take this all as pseudocode -- words meant to describe logic, rather than fit the syntax of the current language -- the following is not C#):

var dog = new Dog():

  1. Initialize all the variables in Dog that we already know the value of (in case we need them during construction):

    static string DogName = "Dog";

  2. Check the constructor and see that we actually need to construct the Dog base class, Animal:

    var animal = new Animal(string name) <- This is where we pass in the DogName

  3. Initialize all the variables in Animal that we already know the value of (in case we need them during construction):

    public string Name;

*(We would then repeat steps 2 and 3 until we hit the base Object class, which we then construct to begin the foundation of our Animal in this case)

  1. Construct our Animal, using the name passed in, which was DogName (from step 2)

    Name = name;

  2. Construct our Dog from that Animal:

    Dog = fresh instance of Animal (We didn't have anything else to do in the Dog constructor)


If we skipped step 1 it would be a bit annoying to have to go all the way back to whichever class actually creates the original value for name.

In a more complex example, if we didn't have it yet, we would need to go back to the previous class to see what was passed in as the parameter in the constructor. If that class didn't have it, we would have to repeat this for every class all the way back to the original one that initializes the referenced value, and then go all the way back to the current constructor we were looking at.

Although that would work, in a sense it also retraces our steps. It is a bit more convenient to step through all the classes we would need and ensure we have all the values we may need along the way, and then once we get to the end of our path, we can return back along it a single time.


Just something fun to add as something to watch out for, which is when you have a member variable that when initialized, initializes forever. If that happens you get a Stack Overflow error because the list of managed values becomes unmanageable, since you're trying to hold all of the initialized values while continuing to the base class.

An example of that would be if you try to create an instance of the following C# class:

public class StackOverflow
{
    public StackOverflow stackOverflow = new StackOverflow();

    public StackOverflow(){ }
}

Upvotes: 3

Related Questions