Export (0) Print
Expand All

9.3.2 Shared Constructors

Visual Studio .NET 2003

Shared constructors initialize a type's shared variables; they are run after the program begins executing, but before any references to a member of the type. A shared constructor specifies the Shared modifier, unless it is in a standard module in which case the Shared modifier is implied.

Unlike instance constructors, shared constructors have implicit public access, have no parameters, and may not call other constructors. Before the first statement in a shared constructor, the shared constructor implicitly performs the initializations specified by the variable initializers of the shared variables declared in the type. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor. The variable initializers are executed in the textual order they appear in the type declaration.

The following example shows an Employee class with a shared constructor that initializes a shared variable:

Imports System.Data

Class Employee
    Private Shared ds As DataSet

    Shared Sub New()
        ds = New DataSet()
    End Sub

    Public Name As String
    Public Salary As Decimal
End Class

Exactly when shared constructors are run is mostly implementation dependent, though several guarantees are provided if a shared constructor is explicitly defined:

  • Shared constructors are run before any instance of a class type is created.
  • Shared constructors are run before any instance members of a structure type are accessed, or before any constructor of a structure type is explicitly called. Calling the implicit parameterless constructor created for structures will not cause the shared constructor to run.
  • Shared constructors are run before any of the type's shared members are referenced.
  • Shared constructors are run before any types that derive from the type are loaded.
  • A shared constructor will not be run more than once during a single execution of a program.

The above guarantees do not apply in the situation where a shared constructor is implicitly created for shared initializers. The output from the following example is uncertain, because the exact ordering of loading and therefore of shared constructor execution is not defined:

Imports System

Module Test
    Sub Main()
        A.F()
        B.F()
    End Sub
End Module

Class A
    Shared Sub New()
        Console.WriteLine("Init A")
    End Sub

    Public Shared Sub F()
        Console.WriteLine("A.F")
    End Sub
End Class

Class B
    Shared Sub New()
        Console.WriteLine("Init B")
    End Sub

    Public Shared Sub F()
        Console.WriteLine("B.F")
    End Sub
End Class

The output could be either of the following:

Init A
A.F
Init B
B.F

or

Init B
Init A
A.F
B.F

By contrast, the following example produces predictable output, because the Shared constructor for the class A must execute before the Shared constructor for the class B, which derives from it:

Imports System

Module Test
    Sub Main()
        Console.WriteLine("1")
        B.G()
        Console.WriteLine("2")
    End Sub
End Module

Class A
    Shared Sub New()
        Console.WriteLine("Init A")
    End Sub
End Class

Class B
    Inherits A

    Shared Sub New()
        Console.WriteLine("Init B")
    End Sub

    Public Shared Sub G()
        Console.WriteLine("B.G")
    End Sub
End Class

The output is:

Init A
Init B
B.G

It is also possible to construct circular dependencies that allow Shared variables with variable initializers to be observed in their default value state, as in the following example:

Class A
    Public Shared X As Integer = B.Y + 1
End Class

Class B
    Public Shared Y As Integer = A.X + 1

    Shared Sub Main()
        Console.WriteLine("X = " & A.X & ", Y = " & B.Y)
    End Sub
End Class

This produces the output:

X = 1, Y = 2

To execute the Main method, the system first loads class B. The Shared constructor of class B proceeds to compute the initial value of Y, which recursively causes class A to be loaded because the value of A.X is referenced. The Shared constructor of class A in turn proceeds to compute the initial value of X, and in doing so fetches the default value of Y, which is zero. A.X is thus initialized to 1. The process of loading A then completes, returning to the calculation of the initial value of Y, the result of which becomes 2.

Had the Main method instead been located in class A, the example would have produced the following output:

X = 2, Y = 1

Avoid circular references in Shared variable initializers since it is generally impossible to determine the order in which classes containing such references are loaded.

See Also

9.3.1 Instance Constructors | 9.3 Constructors | Using Constructors and Destructors (Visual Basic Language Concepts)

Show:
© 2014 Microsoft