I was working with a client recently when I encountered a completely unexpected bug. Figuring out the problem reminded me of some facts about how C# (and all the .NET languages) work and got me thinking about the fact that object-type variables in .NET are basically pointers, even though it’s hidden from you most of the time.
The Problem
An object instance was passed into a method. If the parameter was not null, it was simply updated, but if the parameter was null, then it was instantiated first, then updated.
It looked something like this:
class Widget
{
public string Status { get; set; }
public Widget()
{
Status = "New";
}
}
class Program
{
static public Widget MyWidget { get; set; }
public static void CreateOrUpdateWidget(Widget w)
{
if (w == null) w = new Widget();
w.Status = "Updated";
}
static void Main(string[] args)
{
CreateOrUpdateWidget(MyWidget);
Console.WriteLine(MyWidget.Status);
Console.ReadKey();
}
}
Since it generated no compiler errors or warnings, it seemed perfectly valid at first glance. However, when executed, the Console.WriteLine() line threw a NullReferenceException. It took me a minute to see the problem.
So What Happened?
If you pass an object variable into a method, you are basically passing a POINTER to that object instance. If you set a parameter to a new object instance inside a method, it also updates the pointer stored in the original variable that was passed into the method. Both now point to that new object instance.
However, if you pass in a variable that is NULL and update it within the method, the original variable passed from outside the method is NOT updated! This is because NULL is not a pointer to nowhere, it is the lack of a pointer. The only reason the compiler doesn’t detect this is because, in this example, the null parameter is a property getter.
If you replace the property getter with a local variable, the compiler sees the problem and gives you an error.
class Program
{
public static void CreateOrUpdateWidget(Widget w)
{
if (w == null) w = new Widget();
w.Status = "Updated";
}
static void Main(string[] args)
{
Widget myWidget;
CreateOrUpdateWidget(myWidget);
Console.WriteLine(myWidget.Status);
Console.ReadKey();
}
}