4

Consider the code below:

class Data
{
    public string Name;
    public string NameWithSufix;
}

class Behaviour
{
    private Data data;
    public string Name { get { return data.Name; } private set { } }

    public Behaviour()
    {
        data = new Data()
        {
            Name = "My Name",
            NameWithSufix = Name + " Sufix",
        };
        //data = new Data();
        //data.Name = "My Name";
        //data.NameWithSufix = Name + " Sufix";
    }
}

class Program
{
    static void Main(string[] args)
    {
        Behaviour behaviour = new Behaviour();
    }
}

If you run this program, it will fail with NullReferenceException at Name property. This and this answer and Visual Studio try to persuade me object initializer and object constructors followed by property assignment are the same but it doesn't seem so. If I swap the body of constructor with commented code, it works. It seem like initiliazer doesn't actually run the constructor before it tries to assign properties. Why?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dread Boy
  • 772
  • 6
  • 28
  • It is because properties are initialized before constructors run and you are accessing members of `data` inside of `Name`. – Crowcoder Nov 18 '17 at 12:45

2 Answers2

4

Name in NameWithSufix = Name points to data.Name, from which data is null at the time. A better representation for what object initializers do is this:

Data d = new Data();
d.Name = "My Name";
d.NameWithSufix = this.data.Name /*Name*/ + " Sufix"; // <-- see the problem here

this.data = d;

See that this.data isn't set until the object initializer is done.

This is supported by the C# language specification, as noted by PetSerAl.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • Can you point me to documentation supporting your last statement, please? Visual studio suggests that I shorten constructor+assignment to initializer so I assumed they are the same but apparently they are not. I wish to learn why not. – Dread Boy Nov 18 '17 at 12:54
  • Not sure if it is documented somewhere, but your code clearly shows that is the implementation. – Patrick Hofman Nov 18 '17 at 12:55
  • @DreadBoy You can see [C# Specification](https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#object-initializers), which describes how object initializers processed. – user4003407 Nov 18 '17 at 15:41
  • @PetSerAl, thank you, it says initializer creates temporary variable to hold object and after initialization it copies the value of reference to actual variable. Can you please post that as an answer so I can accept it? – Dread Boy Nov 19 '17 at 08:18
  • @dre not sure if that is helpful since that is the same as my answer. – Patrick Hofman Nov 19 '17 at 12:51
  • I saw you expanded the answer with a link to specs and I accepted it. Thank you! – Dread Boy Nov 19 '17 at 15:01
3

It's running the Behavior constructor first. The problem is that the initialization of the Data class is not yet complete, so the following reference throws an exception

NameWithSufix = Name + " Sufix",

Because it calls get { return data.Name; } but data is still null at this point.


Update:

Patrick Hofman states it better and more accurately in his answer - it's not that the initialization of the Data class isn't complete, but that the new instance has not been assigned to the data variable yet.

Also, he requested a clarification to the official docs - give it a thumbs up if you agree.

Grant Winney
  • 65,241
  • 13
  • 115
  • 165
  • It is not running the constructor first, may want to re-word this. – Crowcoder Nov 18 '17 at 12:49
  • I don't believe that. `data` shouldn't be null because constructor should have been called already. This is what both SO question I linked to tell me. I couldn't find any word about order of operations in official documentation, though. – Dread Boy Nov 18 '17 at 12:52
  • @DreadBoy try the .net documentation instead of SO. Constructors are not run before member initialization. – Crowcoder Nov 18 '17 at 12:53
  • 1
    @Crowcoder, can you help me find it please? By reading this (https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers) I found only "The compiler processes object initializers by first accessing the default instance constructor and then processing the member initializations" This sounds like constructor runs first. When constructors finishes, object should be created, right? – Dread Boy Nov 18 '17 at 12:58
  • @DreadBoy see the CSharp Language specification section 7.6.10.1. Most of what you read about object initializers does not consider what happens before the constructor is run. – Crowcoder Nov 18 '17 at 13:06
  • Well, it does make sense from a concurrency point of view. You can't have half-initiated objects this way. – Patrick Hofman Nov 18 '17 at 13:14
  • 1
    I think the doc should read "The compiler processes object initializers by first accessing the default instance constructor and then processing the member initializations, **and then assign the instance to the variable**" – Patrick Hofman Nov 18 '17 at 13:15
  • The answer of [JaredPar](https://stackoverflow.com/a/740662/5893316) in the linked [question](https://stackoverflow.com/q/740658/5893316) says: _An object initializer is code that runs on an object after a constructor and can be used to succinctly set any number of fields on the object to specified values. **The setting of these fields occurs after the constructor is called**._ – Martin Backasch Nov 18 '17 at 13:15
  • @MartinBackasch That is all about internal (setting the properties from the newly created object), not when that instance is assigned. – Patrick Hofman Nov 18 '17 at 13:16
  • Proposed documentation change: https://github.com/dotnet/docs/pull/3733 – Patrick Hofman Nov 18 '17 at 13:29