Setting property value in constructor VS assigning directly to the property

Viewed 41

I tried but could not find the difference between this:

public class MyClass
{
    public MyClass(){
        MyProperty = "value";
    }

    public string MyProperty { get; set; }
}

and this:

public class MyClass
{
    public MyClass(){
    }

    public string MyProperty { get; set; } = "value";
}

Any information would be great. Even if it's just the name of the second operation.

1 Answers

There are some subtle differences between field initializers and constructors.

First, their order:
The goal of the compiler is to construct an object in a deterministic manner. The compiler will always ensure that all class and instance variables are completely initialized before it will make the instance (this) available for reference.

On instantiation of a class, the compiler will first run all class variable (aka static field) initializers, as class variables can be referenced by both instance variable initializers and instance constructors. If there is no initializer defined, the variable is initialized to the default value of its type.
Initialization of both, class and instance variables, takes place in the order of declaration.

After the static field initializers are run, the class constructor is executed:

class A
{
  private static int StaticField = 2; // #1

  // The compiler must ensure that the static field is already initialized.
  private int instanceField = A.StaticField; // #3

  static A() // #2
  {}
}

Second, the availability of the instance reference (this):

Next, after the class constructor, the instance variable initializers are executed (your second example). At this stage of construction, the compiler has not created a valid instance yet. The instance (this) is only available after all instance fields are initialized and the constructor initializer (this() or base()) has completed.
Therefore, there is still no this reference available.

This means, an instance variable initializer cannot reference other instance variables. Only class variables are allowed (because they were already initialized).

After the instance variable initializers, the instance constructor intializer is executed and then finally the instance constructor body:

class A
{
  // The compiler ensures that the static field is already initialized.
  private int instanceFieldA = A.StaticField; // #3

  private static int StaticField = 2; // #1

  // The reference of an instance at variable initialization will create a compile-time error
  private int instanceFieldB = this.instanceFieldA; // #4

  static A() // #2
  {}

  // The base constructor initializer is executed before the constructor of this.
  // Therefore, the instance is still not ready and cannot be referenced at this point.
  // As a result, the constructor intializers 'base()' and 'this()' cannot reference instance variables.
  public A() : base(this.instanceFIledA) // #5 - Will generate a compile-time error
  {
    // When the constructor body is executed,
    // the instance initializers and constructor initializer are all completed 
    // and the instance can therefore be referenced.  
    int value = this.instanceFieldA;
  }
}

The bottom line, when you need to initialize instance variables based on other instance variable, the initialization is only allowed to take place in the instance constructor.

Also because the instance variable initializers are executed before the constructor initializer (e.g., base()), their initial values in case of virtual members will be available and valid in the superclass (parent class):

class Superclass
{
  virtual string Value { get; } = "From base";

  public Superclass()
  {
    // Will output the value from the subclass' initializer: "From sub"
    Debug.WriteLine(this.Value); 
  }
}

class Subclass : Superclass
{
  override string Value { get; } = "From sub";

  public Subclass()
  {
    // Will output the value from this initializer: "From sub"
    Debug.WriteLine(this.Value); 
  }
}

If in the above example the virtual property had been initialized from the instance constructor (your first example),
the superclass would access a property that would return the default value, which is null in this case (therefore, the superclass would throw):

class Superclass
{
  // The initializer is ignored because the property is overridden by the subclass
  virtual string Value { get; } = "From base";

  public Superclass()
  {
    // Will throw a 'NullReferenceException' because 'Value' is initialized with the default value of type string (NULL).
    Debug.WriteLine(this.Value); 
  }
}

class Subclass : SuperClass
{
  override string Value { get; }

  public Subclass()
  {
    Value = "From sub";

    // Will output: "From sub"
    Debug.WriteLine(this.Value); 
  }
}
Related