Search code examples
c#curly-bracesobject-initializers

Why does this wrong object initialisation with curly braces even compile?


Whille creating some dummy data for a collection for a WPF/MVVM project, I produced the following wrong code, which compiles fine, but throws an exception at runtime.

There's a nested structure of data objects, which I wrongly instantiate with only the curly braces (looks like writing JavaScript does cause permanent damage to the brain).

using System.Collections.ObjectModel;

namespace testapp
{
    class Program
    {
        static void Main(string[] args)
        {
            var collection = new ObservableCollection<TopLevelDataObject>();
            collection.Add(new TopLevelDataObject{Nested = {Bar = 5}});         // NullReferenceException
        }

        class TopLevelDataObject
        {
            public NestedDataObject Nested { get; set; }
            public string Foo { get; set; }
        }

        class NestedDataObject
        {
            public double Bar { get; set; }
        }
    }
}

Why does that compile?

If I create an annonymous type, like Nested = new {Bar = 5}, I get the error message during compilation (which thus fails):

Cannot implicitly convert type '<anonymous type: int Bar>' to 'testapp.Program.NestedDataObject'

Why do I not get such an error when ommitting the new operator?

It even gives me a code hint for the property: click to see the image, for I do not have enough rep to embed it yet

My guess would be that {Bar = 5} is simply a code block, which on its own is a valid thing to have. But why is it valid to assign a code block to anything (in this case, the Nested property)?


Solution

  • Why does that compile?

    Because when that code is compiled, it is compiled as just a set of assignment operations. It doesn't all have to be new instances you create.

    If you construct a new instance of Nested from the constructor, you can assign a value to Nested.Bar.

    Change public NestedDataObject Nested { get; set; } to this to see how it works:

    public NestedDataObject Nested { get; } = new NestedDataObject();
    

    (Note you can never assign a value to Nested outside the constructor in the above code!)