Instantiation And Initialization Order of Classes in C# and Unity

Posted by : on (Updated: )

Category : C#   Unity

Introduction

The order objects are created in C#, usually doesn’t matter to us, because any reference to the object we have, will happen after the object has been created. Until we have a reference to our object, we cannot use any of its public fields, properties and methods.

There are two exceptions to that. An object that has a virtual method call in its constructor and the static fields of the type. In those two cases, the order of creation and initialization of the objects can have different results on the state of the final object.

Let’s see how calls to virtual methods in the constructor can affect a new object’s state, how the order of declaration and initialization of the static fields can do the same, and finally how this order will have different results in a C# script used in a pure C# program, and a C# script used in the Unity ecosystem.

Instantiation And Initialization Order Of C# Types

Let’s start with the creation of C# objects that have an inheritance relationship. This will be the same, for both a normal C# program and a C# script in Unity. Because we should not use constructors in Monobehaviours, the problem with calling virtual methods in constructors applies only to plain C# scripts in Unity and is the same as is for any other program written in C#.

Let’s suppose that we have the following two classes:

public class Parent
{
   public Parent() => Message();

   protected virtual void Message() { }
}

public class Child : Parent
{
   private readonly string _message = "Hello from Initialization";

   public Child() => _message = "HELLO, World!";

   protected override void Message() => Debug.LogError(_message); 
}

and we call:

Parent child = new Child();

The result will be:

Hello from Initialization

The reason for that result is because of the way objects are created and initialized in C#.

In every inheritance relationship, C# creates the objects from bottom to top then executes the constructors from top to bottom. More specifically the order of initialization is like this:

(Child → Parent)

  • Child static fields
  • Child static constructor
  • Child instance fields
  • Evaluation of base-class constructor calls arguments
  • Parent static fields
  • Parent static constructor
  • Parent instance fields

(Parent → Child)

  • Parent instance constructor
  • Child instance constructor

In the previous example, the _message field of the Child class gets initialized, then the constructor of the Parent class gets executed and calls the Message method of the Child, because the method is virtual. The Message method of the Child, will run before the constructor of the Child and for this reason the _message field won’t have the value HELLO, World!.

This behavior of C# is well documented, in many blogs, so I won’t get into further details. The solution is pretty simple, don’t use virtual method calls in the constructor. If you do, and you use resharper, you will see a warning.

If you find yourself in the extra rare situation that a call to a virtual method is needed in the constructor, make sure that the class that makes the call is sealed. This way, you can be sure that there are no derived classes that can be affected from this behavior.

The Evaluation of base-class constructor calls arguments happens when there are parameters that need to be evaluated for the parent class constructor call from the child class, for example:

public Child(int aNumber) : base(aNumber + 1)

So that the constructors have all the necessary information to execute from Parent → Child.

Order Of Initialization Of Static Fields In C# Types

Let’s go, to a lower level, and see what is happening inside a class when it is initialized. Usually, as I mentioned this doesn’t matter to us, because an object has to be fully constructed before we get a reference to it, but there is an exception to that, the static fields of an object.

There is a difference in the behavior between scripts that belong to a C# application and scripts that are used by Unity.

In this paragraph, I will explain what is normally happening in C# and in the next what is happening in C# scripts being used in the Unity environment.

As we have seen, static fields are the first to get initialized. Even if we don’t have an object of our type, sometime before the first time we try to access a static field or method, its static fields will get initialized, and then its static constructor will run.

The static fields will first have their default value, which for all fields is their area in memory filled with zeros, and then will get the value we want to initialize them with, in the order they are declared.

§17.4.5 in ECMA 334: Thus, when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order

An example in code is always better, so let’s suppose that we have the following two cases:

First case:

public class APoco1
{
    public static string World = "World!";
    private static string Message = "Hello " + BPoco1.Message + "World!";
    
    public static void Calculate() =>
        System.Console.WriteLine($"{Message} , {BPoco1.Message}");
}

public class BPoco1 
{
   public static string Message = "amazing " + APoco1.World;
}

Second case:

public class APoco2
{
   
    private static string Message = "Hello " + BPoco2.Message + "World!";
    public static string World = "World!";
    
    public static void Calculate() => 
        System.Console.WriteLine($"{Message} , {BPoco2.Message}");
}

public class BPoco2 
{
   public static string Message = "amazing " + APoco2.World;
}

The only difference is the order of the World and Message fields in the APoco1 and APoco2 classes. Can you guess the outcome of these calls?

APoco1.Calculate();

APoco2.Calculate();

Here’s what we will get:

Hello amazing World!World! , amazing World!

Hello amazing World! , amazing

The result of our call, is different and is dependent on the order our static fields have been declared. This is what is happening:

First Case

In the first case, the APoco1 has both the World and Message strings empty, as all their bits get to zero, then it initializes the World field with the string World!.

After that, tries to initialize the string Message that needs the BPoco1 class.

The Message in BPoco1 class is empty from the creation and then because it is needed it immediately gets initialized to amazing plus the APoco1.World which has already been initialized to World!, so now the BPoco1.Message value is amazing World!.

Finally, the APoco1.Message gets initialized to "Hello " + BPoco1.Message + "World!" which resolves to Hello amazing World!World!

Second Case

In the second case, the APoco2 has both the World and Message strings empty, as all their bits get to zero, then it tries to initialize first the APoco2.Message.

The APoco2.Message, needs the BPoco2 class.

The Message in BPoco2 class is empty from the creation and then because it is needed it immediately gets initialized to amazing plus the APoco2.World.

In this case, the APoco2.World is still empty, it HAS NOT been initialized to the "World!" string, because the APoco2.Message has been declared first, and its initialization has not yet finished.

That means that the BPoco2.Message value is amazing plus empty, which resolves to amazing.

Finally, the APoco2.Message, is "Hello " + BPoco2.Message + "World!" which equals to Hello amazing World! and after that the APoco2.World gets initialized to "World!".

Defined and Undefined Order Of Initialization

Although the order of initialization is defined in C#, coding in such a way that the result is dependent on the order of declaration of our fields, is a serious code smell. Anyone can change the order, because it usually doesn’t matter, or even the IDE can do it automatically based on the rules we have set.

Because the order is defined, we will always have the same result, but there is a case where the order is not defined: If we have partial classes, the order of initialization is undefined and any result cannot be predicted.

Partial classes, are the only case where the order of initialization is not defined in C#, but we have one more case where this is true. The Unity game engine.

Order Of Initialization Of Static Fields In C# Types In Unity

Unity, has two parts. The C++ part and the C# part. When we create a Monobehaviour, this game object has a representation in the C++ space. Let’s look at the following four cases:

1

public class A : MonoBehaviour
{
   public static string World = "World!";
   private static string Message = "Hello " + B.Message + "World!";
   
   public static void Calculate() => Debug.LogError($"{Message} , {B.Message}");
}

public class B : MonoBehaviour
{
   public static string Message = "amazing " + A.World;
}

2

public class Ainverted : MonoBehaviour
{
   private static string Message = "Hello " + Binverted.Message + "World!";
   public static string World = "World!";
   
   public static void Calculate() => Debug.LogError($"{Message} , {Binverted.Message}");
}

public class Binverted : MonoBehaviour
{
   public static string Message = "amazing " + Ainverted.World;
}

3

public class APoco1 
{
   private static string Message = "Hello " + BPoco1.Message + "World!";
   public static string World = "World!";
   
   public static void Calculate() => Debug.LogError($"{Message} , {BPoco1.Message}");
}

public class BPoco1 
{
   public static string Message = "amazing " + APoco1.World;
}

4

public class APoco2
{
   public static string World = "World!";
   private static string Message = "Hello " + BPoco2.Message + "World!";
   
   public static void Calculate() => Debug.LogError($"{Message} , {BPoco2.Message}");
}

public class BPoco2
{
   public static string Message = "amazing " + APoco2.World;
}

The first two have Monobehaviours with the World and Message fields in different order.

The other two, are the same as in the previous paragraph, plain old C# classes with the two fields declared in different order. If we run the following, what would you think the result would be?

public class Test : MonoBehaviour
{
    void Start()
    {
        A.Calculate();
        Ainverted.Calculate();
        APoco1.Calculate();
        APoco2.Calculate();
    }
}

The answer in all the four cases is the same. The result is undefined. In all four cases the result will be one of the following two:

Hello amazing World! , amazing

or

Hello World! , amazing World!

Here’s what is happening. Unity, will initialize the objects internally at its own time, because of its serialization/deserialization mechanism. We can only make guesses without Unity’s source code, but in all cases, Unity seems to fully initialize each type before trying to initialize the next.

For this reason, if A is initialized first, the A.Message will always be "Hello " + B.Message + "World!" with the B.Message empty. This will give the result Hello World!. Then B will be initialized, and the B.Message will be "amazing " + A.World, but because A is initialized the final B.Message will be amazing World!

If B is initialized first, the B.Message will be amazing " + A.World with the A.World empty, which will give the B.Message result amazing. Then A will be initialized, and the A.Message will be "Hello " + B.Message + "World!" with the B.Message equal to amazing that gives us the result Hello amazing World!

Which class will be initialized first is undefined and different every time. In my tests, I had different results after I changed some scripts, different results after upgrading the Unity version and most importantly different results between the editor and the final builds. Even the builds weren’t giving the same results every time a new build was made after some changes.

The results were seemingly random and certainly in contrast with the previous C# examples, where the order is defined, even if it is dependent on the order of declaration of the fields. Here the initialization happens for every class in isolation but the order of initialization of the classes cannot be predicted.

Conclusion

After all this, to sum it up, there are two things we have to avoid when we code in C#.

1) We should not call virtual methods in the constructors of classes that are not sealed.

2) We should not initialize static fields that depend on other static fields that get resolved at runtime.

Wherever possible we should use the const keyword so that we can get consistent results, because the const keyword resolves the fields’ values at compile time.

For example the following:

public class APoco2
{
   
    private static string Message = "Hello " + BPoco2.Message + "World!";
    public const string World = "World!";
    
    public static void Calculate()
    {
        System.Console.WriteLine($"{Message} , {BPoco2.Message}");
    }
}

public class BPoco2 
{
   public const string Message = "amazing " + APoco2.World;
}

will always give the result:

Hello amazing World!World! , amazing World!

No matter the order of declaration of the Message and World fields, both in normal C# programs and in Unity.

Thank you for reading, and as always, if you have any questions or comments you can use the comments section, or contact me directly via the contact form or by email. Also, if you don’t want to miss any of the new blog posts, you can subscribe to my newsletter or the RSS feed.


Follow me: