Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this nested object initializer throw a null reference exception?

The following testcase throws a null-reference exception, when it tries to assign Id to an object which is null, since the code is missing the "new R" before the object initializer.

Why is this not caught by the compiler? Why is it allowed, in which use-cases would this be a meaningful construct?

[TestClass]
public class ThrowAway
{
    public class H
    {
        public int Id { get; set; }
    }

    public class R
    {
        public H Header { get; set; }
    }

    [TestMethod]
    public void ThrowsException()
    {
        var request = new R
                      {
                          Header =
                          {
                              Id = 1
                          },
                      };
    }
}
like image 400
Hugo Hallqvist Avatar asked Jun 03 '15 12:06

Hugo Hallqvist


People also ask

What is a null reference exception?

A NullReferenceException exception is thrown when you try to access a member on a type whose value is null . A NullReferenceException exception typically reflects developer error and is thrown in the following scenarios: You've forgotten to instantiate a reference type.

How do I fix NullReferenceException object reference not set to an instance of an object?

The best way to avoid the "NullReferenceException: Object reference not set to an instance of an object” error is to check the values of all variables while coding. You can also use a simple if-else statement to check for null values, such as if (numbers!= null) to avoid this exception.

What are object initializers?

Object initializers let you assign values to any accessible fields or properties of an object at creation time without having to invoke a constructor followed by lines of assignment statements.


1 Answers

The compiler doesn't give a warning because you could have:

public class R
{
    public H Header { get; set; }

    public R()
    {
        Header = new H();
    }
}

so Header could be initialized by someone/something. Solving if someone/something will initialize Header is a complex problem (probably similar to the Halting problem)... Not something that a compiler wants to solve for you :-)

From the C# specifications:

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.

We are in the case of nested initializer, and see the bolded part. I didn't know it.

Now, note that new R { } is, by C# spec, an 7.6.10.1 Object creation expressions followed by an object-initializer, while the Header = { } is a "pure" 7.6.10.2 Object initializers.

like image 193
xanatos Avatar answered Sep 20 '22 16:09

xanatos