Search code examples
c#typesinstantiationvalue-typereference-type

Instantiation and Initialization of Value Types vs. Reference Types


int number = new int();

Questions:

  1. For reference types, the new operator creates an instance of the type by allocating memory on the heap then initializes it by calling the type's constructor. As seen above, you could do the same for a value type. To me, the line above means that the constructor int() is called to initialize number with a value. I have read that int is a keyword pointing to the struct System.Int32. Therefore, in Visual Studio, I navigate to the struct Int32. Lo and behold, no constructor exists. How exactly is this predefined type initialized to 0 without a constructor?

  2. Related to the above, is there a field in the Int32 struct that stores the value?

  3. Both for custom structs and classes I can create new instances with the new keyword, without the struct or class actually containing a constructor. In that case, is no initialization done at all of the fields the struct/class contains? Is the only thing that happens that memory is allocated on stack/heap for the value/reference type?

  4. Finally, for value types, no new keyword is needed for instantiation and initialization. How exactly does that work on a lower level? Say we do int number = 5;. Is it somehow translated to int a = new int(); a = 5;? If so, how?

Thanks a million!


Solution

  • no constructor [of System.Int32] exists.

    All value types have a default constructor:

    Each value type has an implicit default constructor that initializes the default value of that type.

    (end of quote)

    Related to the above, is there a field in the Int32 struct that stores the value?

    Yes, it does:

    [Serializable]
    [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] 
    [System.Runtime.InteropServices.ComVisible(true)]
        public struct Int32 : IComparable, IFormattable, IConvertible
            , IComparable<Int32>, IEquatable<Int32>
            , IArithmetic<Int32> {
            internal int m_value; // <<== Here it is
    
            public const int MaxValue = 0x7fffffff;
            public const int MinValue = unchecked((int)0x80000000);
            ...
    }
    

    Both for custom structs and classes I can create new instances with the new keyword, without the struct or class actually containing a constructor.

    That is not entirely correct: unlike structs, not all classes have an implicit default constructor; if you define a constructor that takes parameters, the compiler removes the default constructor from your class.

    In that case, is no initialization done at all of the fields the struct/class contains?

    All of the fields that are not initialized explicitly are set to their default values.

    Say we do int number = 5;. Is it somehow translated to int a = new int(); a = 5;?

    int is a built-in type. The compiler knows how to generate IL for it. The type of int is "baked into" the CIL the compiler generates for the assignment:

    .locals init ([0] int32 a)
    ldc.i4.s   5
    stloc.0
    

    The first line corresponds to the declaration of int a, with or without new. The last two lines do the assignment: load the value of 5 into a register, and store it in the local variable a (at index zero).