I found this to be an interesting discovery. I always somewhat expected each .NET language to work in a similar way with regards to things such as field initialization, but as it turns out, C# and VB.NET work differently in this regard, and in both cases it is due to design decisions with the language themselves. To best show this, I created two equivalent class heirarchies in C# and VB.NET
VB.NET:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
Module Module1 Class BaseClass Shared BaseStatic As Integer = 50 Private BaseInstance As Integer = 500 Public Overridable Sub TestSub() Debug.Print("Base TestSub") End Sub Public Sub New(pInt As Integer) Me.BaseInstance = pInt TestSub() End Sub Public Sub New() Me.BaseInstance = 20 End Sub End Class Class DerivedClass Inherits BaseClass Shared DerivedStatic As Integer = 60 Private DerivedInstance As Integer = 600 Public Sub New(pInt As Integer) MyBase.New(pInt) DerivedInstance = 60 End Sub Public Overrides Sub TestSub() Debug.Print(DerivedInstance) End Sub End Class Sub Main() Dim dc As DerivedClass = New DerivedClass(50) End Sub End Module |
C#:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace testinit { class BaseClassTest { private int BaseProperty = 50; public static int BaseStatic = 500; public virtual void testmethod() { } public BaseClassTest(int propertysetting) { BaseProperty = propertysetting; testmethod(); } } class DerivedClassTest :BaseClassTest { private int DerivedProperty = 100; public static int DerivedStatic = 100; public override void testmethod() { Debug.Print(DerivedProperty.ToString()); } public DerivedClassTest(int setting) :base(setting) { DerivedProperty = setting; } } class Program { static void Main(string[] args) { DerivedClassTest testinit = new DerivedClassTest(45); } } } |
Both are effective a base class with a non-default constructor which has a static field and a instance field, and a class derived from that class which implements a similar non-default constructor and sets a derived instance which along with a static field are both initialized at their declaration, then creates an instance of the derived type.
I breakpointed all statements in the program, and traced through each one. in C# the progress was:
- Derived Static field initialized
- Derived Instance field initialized
- Base Static Field Initialized
- Base Instance Field Initialized
- Base Constructor execution
- Derived Constructor Execution
In VB.NET, we have the following order:
- Derived Static Field Initialized
- Base Static Field Initialized
- Base Instance Field Initialized
- Base Constructor Execution
- Derived Instance Field Initialized
- Derived Constructor execution
This has interesting repercussions. for VB.NET the main repercussion is that if you call a virtual (Overridable) method from the base constructor, within that virtual method the instance fields will not be initialized, whereas, with C#, they will be. On the C# side, it is likely the reason that derived instance fields cannot be initialized by calling an instance method, because the initialization of instance fields occurs before the base class static and instance fields have been initialized, so the class state within that method would be difficult to predict. Arguably, it seems that Visual Basic .NET takes a more “You probably know what you are doing” approach, whereas C# is more careful and “safety” conscious.
Have something to say about this post? Comment!