Savage.org.za

Tall Stories

Monday, 4 May 2009

Inherited C# and VB.Net Objects Initialize Fields in a Different Sequence

If you’ve ever run FxCop and seen rule #CA2214 “Do not call overridable methods in constructors”, here’s a good example of why you should avoid this mistake. Consider the following contrived example code in C#:

Firstly the base class that contains the virtual call in the constructor:

   1: abstract class BaseClass
   2: {
   3:     public BaseClass()
   4:     {
   5:         SomeInitialization();
   6:     }
   7:  
   8:     public abstract void SomeInitialization();
   9: }


Then the class that inherits from BaseClass overrides the SomeInitialization function and accesses a field in the subclass:



   1: class DerivedClass : BaseClass
   2: {
   3:     object foo = new object();
   4:  
   5:     public override void SomeInitialization()
   6:     {
   7:         foo.ToString();//foo has been initialized
   8:     }
   9: }

Creating a new instance of DerivedClass succeeds because



  1. Field “foo” is initialized with “new object()”

  2. The constructor of BaseClass is called which in turn calls

  3. The virtual “SomeInitialization” in DerivedClass.

However in VB.Net what appears to be the same code, will in fact fail:


The base class:



   1: MustInherit Class BaseClass
   2:     Public Sub New()
   3:         SomeInitialization()
   4:     End Sub
   5:  
   6:     Public MustOverride Sub SomeInitialization()
   7: End Class


The derived class:



   1: Class DerivedClass
   2:     Inherits BaseClass
   3:  
   4:     Private foo As New Object()
   5:  
   6:     Public Overrides Sub SomeInitialization()
   7:         foo.ToString() 'This line will fail because x has not been initialized.
   8:     End Sub
   9: End Class

In the Visual Basic code, the sequence is



  1. The constructor of BaseClass is called which in turn calls

  2. The virtual “SomeInitialization” in DerivedClass.

  3. Field “foo” is initialized with “new object()”

But it will never reach step 3 because step 2 fails with a NullReferenceException. Also be careful of naive fixes like the following:



   1: Class NaiveFix
   2:     Inherits BaseClass
   3:  
   4:     Private x As New List(Of String)
   5:  
   6:     Public Overrides Sub SomeInitialization()
   7:         x = New List(Of String)
   8:         x.Add("A Value")
   9:     End Sub
  10: End Class

In this case field “x” is initialized inside the “SomeInitialization” function, an item added to the list, and is then re-initialized once the constructor sequence has completed, resulting in an empty list. This is one of those sneaky language differences to catch the unwary.

Labels: , ,