3

Why there's a NullReferenceException when trying to set value of X in the code below? It works fine when I use new keyword when initializing B, but why it compiles fine without new and then fails during runtime?

https://dotnetfiddle.net/YNvPog

public class A
{
    public _B B;
    public class _B
    {
        public int X;
    }
}

public class Program
{
    public static void Main()
    {
        var a=new A{
                B={
                    X=1
                }
            };
    }
}
4
  • Specifically see the section Indirect in the accepted answer. Commented Oct 21, 2015 at 17:21
  • 1
    Because in your example you do not initialize B. You basically do a.B.X =1 where B is still null. Commented Oct 21, 2015 at 17:22
  • Are you asking why did you get a NRE or why the compiler doesn't warn you about using the B variable without instancing it before? Commented Oct 21, 2015 at 17:23
  • Terrible compiler! it should detect that B={} will return null. It is very hard to find when you have multiple nested code. I enabled Common Language Runtime Exceptions and it will say something like this [namespace].A.B.get returned null.. It is easy fix B = new B{} Commented Feb 15, 2018 at 20:00

1 Answer 1

6

Initialization syntax can be tricky. In your code, you're trying to set the value of a.B.X without first setting the value of B. Your code translates to:

var a = new A();
a.B.X = 1;

... which would produce the same exception you're getting now. That's because a.B is initialized to null unless you explicitly create an instance for it.

As you noted, this will work:

    var a=new A{
            B= new _B {
                X=1
            }
        };

You could also make sure that A's constructor initializes a B.

    public _B B = new A._B();

why it compiles fine without new and then fails during runtime?

It would require too much work for the compiler to dig into the code for your A class and realize that B will definitely be null at this point in time: as I pointed out you could change the implementation of A's constructor to make sure that's not the case. This is one reason that null reference exceptions are the most common type of exception out there.

The best strategy to avoid this is to initialize all of your fields to non-null values in the constructor. If you won't know what value to give them until your constructor is invoked, then make your constructor take those values as parameters. If you expect one of your fields may not always have a value, you can use an optional type like my Maybe<> struct to force programmers to deal with that fact at compile-time.

Update 2021

Now C# supports nullable reference types, which you can use to encourage/force programmers to know that your field could be null, if that's the route you want to take.

    public _B? B;
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, I thought it's something similar like int[] a={1,2,3}. After I posted the question I added keyword "nested" and found it in specs: "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."
@Franta: I know what you mean. Also note that list initializer syntax invokes "Add" on the collection, rather than creating a new list. So it's usually wise to always initialize List properties with empty lists in the constructor.
I tried to init an array of ints: dotnetfiddle.net/SNlACj Then it throws compilation error: "Cannot initialize object of type 'int[]' with a collection initializer" (4.5) and "'int[]' does not contain a definition for 'Add'" (Roslyn). Thanks for explanation - I guess I'd be perplexed from those error messages not knowing what's going on.
@StriplingWarrior Your link is unfortunately dead. Perhaps provide a new one?
@Heki: Thanks for pointing that out. I moved the library to Github. However, with C#'s addition of nullable reference types, I think that library is far less useful than it used to be. I updated my answer accordingly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.