What is the purpose of a static constructor?

12

I've always used constructors as follows:

public MinhaClasse()
{
  //Algo
  ...
}

However, I discovered that in C # it is possible to create a static constructor as follows:

public class MinhaClasse
{
    public string Propriedade { get; set; }

    static MinhaClasse() 
    {
        //Algo
        ....
    }
}

And the class with the static constructor is instantiated in the same way as a common class:

MinhaClasse c = new MinhaClasse();
c.Propriedade = "Hello Stackoverflow";
WriteLine(c.Propriedade);

Output:

  

Hello Stackoverflow

Questions

What is the purpose of a static constructor and what are the differences of the static constructor compared to the default constructor?

    
asked by anonymous 03.07.2016 / 23:39

2 answers

6

I will not go into many details because various information pertinent to the subject has already been answered in other questions. I'm imagining that you understand the difference between static members and instance members .

The static constructor serves to initialize the static members of the class (as opposed to initializing the members of the instantiated object). And it is called exclusively by CLR at some point before some static member is used, following rules set out in the specification ( if all goes well it will only be called once and there is no way to control your call). There can only be one constructor without a parameter.

Since it is common for members to be initialized on their own, we rarely need to write them. But there may be some situations where the compiler can not evaluate the initialization expression, may need a specific boot order (one depends on the other), do something extra besides boot, then it can be useful.

Its utility is the same as an instance constructor (it can be seen in linked questions below), no more or less, it only changes the members it can manipulate.

Actually the compiler also generates a static constructor whenever there is some initialization of static members. Initialization can not occur magically, any code to be executed must always be within a method, in this case the static constructor.

public class Exemplo {
    static int x = 1;
}

In the background something like this will be generated:

public class Exemplo {
    static int x;
    static Exemplo() { x = 1; }
}

IL code generated for the code of the question (note the existence of two ctor , one normal and another instance and note beforefieldinit ):

.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi beforefieldinit Program
    extends [mscorlib]System.Object
{
    // Methods
    .method public hidebysig static 
        void Main () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 32 (0x20)
        .maxstack 2
        .locals init (
            [0] class MinhaClasse
        )

        IL_0000: nop
        IL_0001: newobj instance void MinhaClasse::.ctor() //   <========= note a chamada ao método de instância
        IL_0006: stloc.0
        IL_0007: ldloc.0
        IL_0008: ldstr "Hello Stackoverflow"
        IL_000d: callvirt instance void MinhaClasse::set_Propriedade(string)
        IL_0012: nop
        IL_0013: ldloc.0
        IL_0014: callvirt instance string MinhaClasse::get_Propriedade()
        IL_0019: call void [mscorlib]System.Console::WriteLine(string)
        IL_001e: nop
        IL_001f: ret
    } // end of method Program::Main

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x207c
        // Code size 8 (0x8)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: nop
        IL_0007: ret
    } // end of method Program::.ctor

} // end of class Program

.class public auto ansi MinhaClasse
    extends [mscorlib]System.Object
{
    // Fields
    .field private string '<Propriedade>k__BackingField'
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
        01 00 00 00
    )
    .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = (
        01 00 00 00 00 00 00 00
    )

    // Methods
    .method public hidebysig specialname 
        instance string get_Propriedade () cil managed 
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x2085
        // Code size 7 (0x7)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldfld string MinhaClasse::'<Propriedade>k__BackingField'
        IL_0006: ret
    } // end of method MinhaClasse::get_Propriedade

    .method public hidebysig specialname 
        instance void set_Propriedade (
            string 'value'
        ) cil managed 
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x208d
        // Code size 8 (0x8)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldarg.1
        IL_0002: stfld string MinhaClasse::'<Propriedade>k__BackingField'
        IL_0007: ret
    } // end of method MinhaClasse::set_Propriedade

    .method private hidebysig specialname rtspecialname static  
        void .cctor () cil managed   //<============== construtor estático aqui
    {
        // Method begins at RVA 0x2096
        // Code size 2 (0x2)
        .maxstack 8

        IL_0000: nop
        IL_0001: ret
    } // end of method MinhaClasse::.cctor

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed   //<============== construtor de instância aqui
    {
        // Method begins at RVA 0x207c
        // Code size 8 (0x8)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: nop
        IL_0007: ret
    } // end of method MinhaClasse::.ctor

    // Properties
    .property instance string Propriedade()
    {
        .get instance string MinhaClasse::get_Propriedade()
        .set instance void MinhaClasse::set_Propriedade(string)
    }

} // end of class MinhaClasse

Avoid use

It is advisable to avoid use as much as possible especially if you want performance.

It is a danger to do something that generates an exception in a static constructor. Whoever called has to capture, right? It was not his code that called him, it was the virtual machine. Disaster in sight (class becomes invalid - not instances).

It looks like the static constructor, but it is not.

Actually in the query example what you are calling is not the static constructor, it is the parameterless instance constructor that the compiler generates for you ( default constructor ) so that the class can be instantiated in any situation. The instance constructor is for initializing instance members.

Difference between them

Basically it is what they manipulate and how they are called. But there are small differences, such as how you can define them, for example.

More information:

03.07.2016 / 23:52
6

A static constructor in C # can never be called directly through code (in the case of your application a non-static constructor implicitly created by the compiler).

The static constructor is called before the first use of the class, be it a static call or through object to this class.

The main purpose of this type of constructor is initialization of static members automatically, considering for example that it is necessary to call a specific method to initialize a static property of this class this could be done manually (the programmer's chance of forgetting to do it and corrupt the class) or by invoking the static constructor by the application automatically when the static call to this class is invoked (specifically before the call).

Complementing further, as the other answers put it: The static constructor can not contain parameters and can not be manually invoked by the class user, so it will not be impacted by public, protected, and private modifiers either.

    
03.07.2016 / 23:50