Search code examples
c#classobjectreferencenullreferenceexception

A NULL object in a List stays NULL even after initialization


Please take a look at the code below:

public class MainClass
{
    MyClass myObject1, myObject2;

    public MainClass()
    {
        myObject1 = new MyClass();
        myObject1.otherObjectsOfSameClass = new List<MyClass> { myObject2 };

        myObject2 = new MyClass();
        myObject2.otherObjectsOfSameClass = new List<MyClass> { myObject1 };
        
        myObject2.otherObjectsOfSameClass[0].TestMethod(); // works fine
        myObject1.otherObjectsOfSameClass[0].TestMethod(); // throws null reference exception
    }
}

class MyClass
{
    public List<MyClass> otherObjectsOfSameClass;

    public bool TestMethod()
    {
        return true;
    }
}

What I am trying to achieve here is to store several objects of MyClass type that hold references to the other members of the same type. When I initialize the otherObjectsOfSameClass list of each object in the MainClass constructor, I add the other object to that list. When I initialize the myObject1, the myObject2 is still null - that is expected. The list should hold a reference to myObject2, even if it's null.

However, even after I initialize myObject2, it is still null in the otherObjectsOfSameClass list of the myObject1. For some reason, the object reference in that list doesn't update. Can someone please explain this behavior? Am I misunderstanding something fundamental here?

EDIT: I realized this can be reproduced with an even simpler test code:

        List<MyClass> myList = new List<MyClass> { myObject1, myObject2 };

        myObject1 = new MyClass();

        myObject1.TestMethod(); // works fine
        myList[0].TestMethod(); // throws null reference exception

It seems that adding an uninitialized object to a list and initializing it afterwards doesn't change it in the list - even though it should be added by reference, not by value...


Solution

  • Variables and elements of lists store values. For a reference type like MyClass, That value is either a reference to an object, or the value null - nothing.

    In the working case,

    myObject2.otherObjectsOfSameClass = new List<MyClass> { myObject1 };
    

    The list contains the reference that the variable myObject1 stores. myObject1 stores a reference to a MyClass object, not null, so the list also stores a reference to that same object, so when you access the list, no exceptions. Note that the values of myObject1 and myObject2.otherObjectsOfSameClass[0] are unrelated. It just so happens that their values are references to the same object, but otherwise changing the value of one of them would not affect the other. For example, doing:

    myObject1 = null;
    

    would not make myObject2.otherObjectsOfSameClass[0] null as well.

    OTOH, when this is run:

    myObject1.otherObjectsOfSameClass = new List<MyClass> { myObject2 };
    

    myObject2 stores null, so the list stores null. Again, the values of myObject2 and myObject1.otherObjectsOfSameClass[0] are unrelated. It just so happens that they are both null. Then you say:

    myObject2 = new MyClass();
    

    which changes the value of myObject2 to a reference to an object. This has no effect on the value of myObject1.otherObjectsOfSameClass[0]. After all, they are two different variables.