C# complex type initializer compiles without new keyword
The C# Specification 5.0 defines object initializer as (7.6.10.2 Object initializers):
An object initializer specifies values for zero or more fields or properties of an object.
object-initializer: { member-initializer-listopt } { member-initializer-list , }
And after the detailed explanation there is an example given which is very similar to your code:
If Rectangle’s constructor allocates the two embedded Point instances
public class Rectangle { Point p1 = new Point(); Point p2 = new Point(); public Point P1 { get { return p1; } } public Point P2 { get { return p2; } } }
the following construct can be used to initialize the embedded Point instances instead of assigning new instances:
Rectangle r = new Rectangle { P1 = { X = 0, Y = 1 }, P2 = { X = 2, Y = 3 } };
which has the same effect as
Rectangle __r = new Rectangle(); __r.P1.X = 0; __r.P1.Y = 1; __r.P2.X = 2; __r.P2.Y = 3; Rectangle r = __r;
But there is only one difference, the Point
instances here are initialized inside of the Rectangle
class which occurs in the constructor of Rectangle
.
So the syntax is valid by the specification, but you need to make sure Value
is initialized before using the object initializer to initialize its properties in order to avoid NRE.
An object-initializer doesn not really instantiate your members.
See the following code:
var myInstance = new MyInstance { MyMember = new MyMember { Value = 3 }; }
This compiles to:
var myMember= new MyMember();
myMember.Value = 3;
var myInstance = new MyInstance();
myInstance.MyMember = myMember;
In your case you forgot to instantiate MyMember
, thus the object-intializer tries to access that property and assign further values to it. This is due to the fact that object-initializers allways run after the appropriate constructor, which wasn´t called in your case. So in your case it compiles to this:
var myInstance = new MyInstance();
myMymber.Value = 3;
Causing a NullReferenceException
as myMember
was never instantiated.
Why does this even compile? Well, I assume the compiler assumes that you instantiate MyMember
within the constructor of MyInstance
. It can´t know wheather you actually did this.
class Instance
{
MyMember MyMember = new MyMember();
}
Leaving members null
is of course absoluetely valid.
The object initializer syntax allows you to initialize an object without creating it first. This is rather important if you want to preserve object identity.
For example, you could make ClassA.Value
a read-only property, and initialize it in the object constructor:
public class ClassA
{
public ClassA()
{
Value = new MyDecimal();
}
public MyDecimal Value { get; private set; }
}
This behaviour is of course explicitly outlined in the C# specification (excerpt from version 5):
7.6.10.2 Object initializers
A member initializer that specifies an expression after the equals sign is processed in the same way as an assignment (§7.17.1) to the field or property.
A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.
Since your Value
initializer is a nested initializer, it allows you to just assign members of Value
without initializing it - as long as Value
has been initialized already, of course. The compiler has no way of verifying whether Value
is null
, so it cannot give you an error.