Ron Beyer
Ron Beyer

Reputation: 11273

Automatic initialization of backing fields

This question is not about how to initialize backing fields...

Lets say given this class:

public class Test
{
    public int PropertyA { get; set; }
    public int PropertyB { get; set; }
    private int _propertyC;
    public int PropertyC { get { return _propertyC; } set { _propertyC = value; } }
}

There are a number of properties defined, both automatically and explicitly implemented.

Lets take a closer look at how PropertyA gets compiled (64-bit debug). The MSIL output is this:

.field private int32 '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 )

.property instance int32 PropertyA()
{
  .get instance int32 TestCode.Test::get_PropertyA()
  .set instance void TestCode.Test::set_PropertyA(int32)
} // end of property Test::PropertyA

.method public hidebysig specialname instance int32 
        get_PropertyA() cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldfld      int32 TestCode.Test::'k__BackingField'
  IL_0006:  ret
} // end of method Test::get_PropertyA

.method public hidebysig specialname instance void 
        set_PropertyA(int32 'value') cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       8 (0x8)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  stfld      int32 TestCode.Test::'k__BackingField'
  IL_0007:  ret
} // end of method Test::set_PropertyA

Which is pretty basic, it defines the backing field, the property and then the get_ and _set methods for that property that load or set the backing field.

What I don't get (and I've taken for granted up until now), is that this is a perfectly legal constructor:

public Test()
{
    Debug.WriteLine($"{PropertyA.ToString()}");
    Debug.WriteLine($"{_propertyC.ToString()}");
}

While this is obviously a compiler error:

public Test()
{
    int i;
    Debug.WriteLine($"{i.ToString()}");
}

Which, as expected, gives CS0165 "Use of unassigned local variable 'i'". In doing some research for this and digging through the ECMA Spec, and the Microsoft C# spec (5.0 is the last formal document, 6.0 seems to be loosely distributed in the Roslyn project) and I can't find anything related to the initialization of fields.

I did find this reference from VS2003 that eludes to class fields being initialized to the default value for the type, but it suggests that this is part of the language and not the runtime.

So the question is... Does the runtime perform the initialization of the fields, or am I missing something in the MSIL from the compiler that is initializing these values? I'm assuming the compiler does this otherwise I would expect the same CS0165 in both the example constructors.

Upvotes: 3

Views: 289

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149518

Does the runtime perform the initialization of the fields, or am I missing something in the MSIL from the compiler that is initializing these values?

Part §5.2 and §5.3 of the specification talk about default values and definite assignment, respectively.

A quote from the spec (§5.2):

Initialization to default values is typically done by having the memory manager or garbage collector initialize memory to all-bits-zero before it is allocated for use. For this reason, it is convenient to use all-bits-zero to represent the null reference.

This goes on to show the memory manager and GC take care of default value initialization.

Upvotes: 1

Related Questions