简体   繁体   中英

C# when do instance variables with default values get set?

I've always wondered this. In C#, when do instance variables with default values get set - before or after the constructor?

For this code:

public class Foo
{
  public int bar=1;
  public Foo()
  {
    bar = 2;
  }
}
...
Console.WriteLine(new Foo().bar);

Would it output 1 or 2?

The instance during construction will run through all declarations first then proceed into the constructor itself.

Thus, bar = 1 will execute before bar = 2 but the end result will still be bar = 2.

它将返回2,您将在构造函数对其进行修改后访问该属性。

The member instantiations are added first in the constructor. If you check out the IL code that is actually created using Reflector, this is what the compiler does with it:

.class auto ansi nested public beforefieldinit Foo
  extends [mscorlib]System.Object
{
  .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
  {
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldc.i4.1 
    L_0002: stfld int32 Test.Program/Foo::bar
    L_0007: ldarg.0 
    L_0008: call instance void [mscorlib]System.Object::.ctor()
    L_000d: nop 
    L_000e: nop 
    L_000f: ldarg.0 
    L_0010: ldc.i4.2 
    L_0011: stfld int32 Test.Program/Foo::bar
    L_0016: nop 
    L_0017: ret 
  }

  .field public int32 bar

}

So, the member instantiations are placed first in the constructor, then it calls the constructor of the base type, then comes the code that you put in the constructor.

Why don't you test this?

When instantiating a class object, you essentially create an instance of the class, then run the constructor function. Therefore, it should output 2

The underlying bytecode doesn't have any special ability to initialize these values before calling a constructor. The C# compiler is responsible for the ordering, which is emitted as bytecode in a method called .ctor for the type:

If the constructor does not specify this(...) as a chained constructor, meaning it either specifies base(...) or nothing at all, then it:

  1. Set the value for all fields with initializers.
  2. Calls the constructor for the base class. If no specific one is specified, it calls the default one.
  3. Runs the code you typed in your constructor.

If the constructor specifies this(...) as a chained constructor, the order is slightly different because the chained constructor will handle the initialization of fields.

  1. Call the chained constructor, which is the one you explicitly indicated with : this(...) .
  2. Run the code you typed in your constructor.

If this didn't happen, it could create some very confusing and unintuitive situations. Like if something in your constructor acted in a particular way depending on the initial value of bar, it would have no idea if it had been initialised (or worse, bar was a user defined class rather than an int. Is it null because the constructor of that class wanted it to be? Or has it just not been initialised?) It helps to think of constructors as the birth process of you class, like the point a baby actually pops out of it's Mummy, and initialisation as the growing in the womb. Pre-conditions such as initialisation are a given - you wouldn't see a baby born with no arms, and then suddenly they appeared an hour later would you?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM