What happens when a constuctor is called


Consider the following class

public class MyClass
{
    public int myField = 4;
 
    public MyClass()
    {
        this.myField = 5;
    }
}

 when we compile this class, we get an assembly with IL in it. The parameterless constructor gets converted into a method with the name .ctor

Now, if we see the IL that gets generated for this .ctor method, we might expect it to have IL opcodes to assign the value 5 to the myField field of the class. 
However, we can see that there is a lot more in there.
First of all, we see IL commands to assign the value 4 to myField, then the .ctor method of System.Object gets called. Finally, we see the code within the MyClass’s constructor.
When we write code to instantiate this class using the new operator (i.e. MyClass myObject = new MyClass();) the following things happen in the given order: -
1.       Execution of the initializers of all the class’s fields
2.       Execution of the class’s base class constructor
3.       Execution of the code within this class constructor
Let us create another class MyDerivedClass and derive it from MyClass as below. 
public class MyDerivedClass : MyClass
{
    public int myDerivedProperty = 6;

    public MyDerivedClass()
    {
        this.myDerivedProperty = 7;
    }
}
When we instantiate MyDerivedClass, by the same flow as earlier, the following things happen in order
1.       myDerivedProperty gets initialized to 6
2.       the constructor of MyClass gets called
a.       MyClass.myField gets initialized to 4
b.      The constructor of System.Object gets called
c.       MyClass.myField is assigned the value 5 due to the code in MyClass’s constructor
3.       myDerivedProperty is assigned the value 7 due to the code in MyDerivedClass’s constructor

Addendum

Thought, I'd the IL that gets generated for the constructor.

Looking at the C# code written in the constructor, we might expect to see code somethign like below

.method public hidebysig specialname rtspecialname

        instance void  .ctor() cil managed

{

  // Code size       17 (0x11)

  .maxstack  8

  IL_0000:  ldarg.0

  IL_0001:  ldc.i4.7

  IL_0002:  stfld      int32 ConsoleApplication5.MyDerivedClass::myDerivedProperty

  IL_0007:  nop

  IL_0008:  ret

} // end of method MyDerivedClass::.ctor 

 However, what we see is something like below

.method public hidebysig specialname rtspecialname

        instance void  .ctor() cil managed

{

  // Code size       24 (0x18)

  .maxstack  8

  IL_0000:  ldarg.0

  IL_0001:  ldc.i4.6

  IL_0002:  stfld      int32 ConsoleApplication5.MyDerivedClass::myDerivedProperty

  IL_0007:  ldarg.0

  IL_0008:  call       instance void ConsoleApplication5.MyClass::.ctor()

  IL_000d:  nop

  IL_000e:  nop

  IL_000f:  ldarg.0

  IL_0010:  ldc.i4.7

  IL_0011:  stfld      int32 ConsoleApplication5.MyDerivedClass::myDerivedProperty

  IL_0016:  nop

  IL_0017:  ret

} // end of method MyDerivedClass::.ctor

i.e. first the variable initializer gets executed, then the base constructor gets called and finally we have the IL corresponding to the C# code written in the class constructor

 
Comments (2)
  1. Neelam Gupta says:

    Correct

  2. Zing says:

    I think Alvaro made it sure to post only the correct information in the blog

Comments are closed.

Skip to main content