Wednesday, March 24, 2010

Field initializer differences between C# and VB.NET

There are many subtle problems which exist when porting code between C# and VB.NET. One involves inheritance and the order of initialization for fields in a class. I learned about this from reading .NET Gotcha’s by Venkat Subramaniam (Gotcha #27). In C#, fields initializers are executed in order from child to parent and then constructors are called from parent to child. In VB.NET, field initializers are executed immediately before the constructor of the relevant. As with C#, constructors are called from parent to child. Here is a simple example that illustrates how different behavior can result.


The VB.NET code looks like this:
 Public Class Sample1  
Public Sub New()
Debug.WriteLine("Sample1 constructor")
End Sub
End Class
Public Class Sample2
Public Sub New()
Debug.WriteLine("Sample2 constructor")
End Sub
End Class
Public Class Parent
Public oSam1 As New Sample1
Public Sub New()
Debug.WriteLine("Parent constructor")
End Sub
End Class
Public Class Child
Inherits Parent
Public oSam2 As New Sample2
Public Sub New()
Debug.WriteLine("Child constructor")
End Sub
End Class

The C# code looks like this:
   class Sample1  
{
public Sample1()
{
Debug.WriteLine("Sample 1 constructor");
}
}
class Sample2
{
public Sample2()
{
Debug.WriteLine("Sample 2 constructor");
}
}
class Parent
{
public Sample1 Sam1 = new Sample1();
public Parent()
{
Debug.WriteLine("Parent constructor");
}
}
class Child:Parent
{
public Sample2 sam2 = new Sample2();
public Child()
{
Debug.WriteLine("Child constructor");
}
}

Despite the seemingly identical code, the order of execution varies significantly.
In VB.Net the output is:

  1. Sample1 constructor

  2. Parent constructor

  3. Sample2 constructor

  4. Child constructor


But in C# the output is:

  1. Sample 2 constructor

  2. Sample 1 constructor

  3. Parent constructor

  4. Child constructor


There are a couple of approaches that allow this concern to be resolved easily. One simple solution to this problem is to separate construction of objects from effectively initializing their state. For example, having the classes Sample1 and Sample2 put their logic in an Init method would allow a more consistent and predictable behavior. Another approach is to assign the reference (i.e. “new up”) to data fields within the constructor. I expound on the more general flaw of doing too much in constructors here.

No comments:

Post a Comment