GOTCHA #27 Object initialization sequence isn't consistentWhen you create an object, the memory for the instance is allocated, each of its fields is initialized with the default value defined by the CTS, and then the constructor is invoked. If you create an object of a derived class, then all fields of the base are initialized and the constructor of the base is invoked before any field of the derived class is initialized. This is conventional wisdom derived from languages such as C++ and Java. But it is not the sequence that is followed in C#. In fact, the sequence of initialization is not the same between C# and VB.NET. Take a look at Example 3-20.
Example 3-20. Object initialization sequenceC# (Initialization) //SomeClass1.cs using System; namespace ObjectInitSequence { public class SomeClass1 { public SomeClass1() { Console.WriteLine("Constructor of SomeClass1 called"); } } } // SomeClass2.cs using System; namespace ObjectInitSequence { public class SomeClass2 { public SomeClass2() { Console.WriteLine("Constructor of SomeClass2 called"); } } } //Base.cs using System; namespace ObjectInitSequence { public class Base { private SomeClass1 obj1 = new SomeClass1(); public Base() { Console.WriteLine("Constructor of Base called"); } } } //Derived.cs using System; namespace ObjectInitSequence { public class Derived : Base { private SomeClass2 obj2 = new SomeClass2(); public Derived() { Console.WriteLine("Constructor of Derived called"); } } } //Test.cs using System; namespace ObjectInitSequence { class Test { [STAThread] static void Main(string[] args) { Derived obj = new Derived(); } } } VB.NET (Initialization) 'SomeClass1 Public Class SomeClass1 Public Sub New() Console.WriteLine("Constructor of SomeClass1 called") End Sub End Class 'SomeClass2.vb Public Class SomeClass2 Public Sub New() Console.WriteLine("Constructor of SomeClass2 called") End Sub End Class 'Base.vb Public Class Base Private obj1 As SomeClass1 = New SomeClass1 Public Sub New() Console.WriteLine("Constructor of Base called") End Sub End Class 'Derived.vb Public Class Derived Inherits Base Private obj2 As SomeClass2 = New SomeClass2 Public Sub New() Console.WriteLine("Constructor of Derived called") End Sub End Class 'Test.vb Module Test Sub Main() Dim obj As Derived = New Derived End Sub End Module In the above code, the class Base has a field of type SomeClass1. The class Derived, which inherits from Base, has a field of type SomeClass2. Each of these classes has a constructor that prints a message announcing itself. What is the sequence of field initialization and constructor calls when an object of Derived is created? Before you answer, you may want to ask, which language?! The C# code given above produces the output shown in Figure 3-16. Figure 3-16. Output from C# version of Example 3-20However, the VB.NET version of the code produces different results, shown in Figure 3-17. Figure 3-17. Output from VB.NET version of Example 3-20While the two programs are identical except for the language used to write them, the behavior is different. In C#, the Derived class's fields are initialized, then those of the Base class. Next, the constructors are called top-down, the Base constructor first and then the Derived constructor. In the case of the VB.NET program, however, the sequence is different (and conformant with the sequence in C++ and Java). The initialization of fields in Base and the invocation of the Base class constructor complete before any field of the Derived class is initialized. While you are wondering about this, let me throw you some even more interesting things. What is the sequence if I derive a C# class from a VB.NET class? What happens if I derive a VB.NET class from a C# class which in turn is derived from another VB.NET class? If I derive a C# class from a VB.NET class, then the derived members will be initialized before the base members. However, if I derive a VB.NET class from a C# class, then the base members will be initialized before any derived members. In case you have more than two levels of inheritance and you mix languages between levels, the sequence depends on the language of the derived class at each level (good luck). IN A NUTSHELLClearly understand the sequence in which objects are initialized in C# versus VB.NET. Understanding the sequence will help avoid surprises from this rather odd inconsistency. SEE ALSOGotcha #23, "Copy Constructor hampers exensibility," Gotcha #24, "Clone() has limitations," and Gotcha #28, "Polymorphism kicks in prematurely." |