“Object Reference Not Set to an instance of an object.” Cast the first stone for those who never struggled with this error message as a beginner C# / .NET programmer.
This infamous and dreaded error message happens when you get a NullReferenceException. This exception throws when you attempt to access a member. For instance, a method or a property on a variable that currently holds a null reference.
But what does null reference exactly mean? What exactly are references? And how can you stop the NullReferenceException occurring in your code? Let’s take a look.
We’ll start with fundamentals by briefly explaining what references are in C# / .NET. After that, you’ll learn what null references are.
Following our exploration of theoretical definitions, we will dig into a more hands-on approach. This will guide you on preventing the occurrence of the NullReferenceException in real-world applications.
We already know that a null reference causes the NullReferenceException. But in what way does it differ from a non-null reference?
In .NET, data types are categorized into two groups: value types and reference types. A value type variable stores the actual value, whereas a reference type variable holds a reference pointing to the location of the object in memory. This reference functions like a link or shortcut, providing access to a web page or file on your computer, helping you understand where the object resides.
Types such as int (and the other numerical primitive types), DateTime, and boolean are value types. That is, structs are value types. Classes are reference types.
So, a reference is what a variable of a reference type contains. Variables can become empty, which we call a null reference: a reference that doesn’t point to any object. When you try calling a method or another member on an empty variable, you get the NullReferenceException.
Null reference errors are responsible for a good percentage of all application bugs. Null references are often problems that arise from the lack of additional logic to verify if objects possess valid values before utilizing those objects. Here are some ways a NullReferenceException can occur:
If the variable “text” is passed in as null to the function MyMyethod, the following code will throw a NullReferenceException. You cannot invoke the ToUpper() method on a string that is null.
public void MyMethod(string text)
{
//Throws exception if text == null
if (text.ToUpper() == “Hello World”)
{
//do something
}
}
You can also have null reference exceptions because any object type is null, not just string. For example, the code below never modifies the SqlCommand object.
Not running a SQL query would be a serious problem for your application. A null string might be something you just ignore and move on. Sometimes, especially with SqlCommand, it indicates a serious issue.
SqlCommand command = null;
//Exception! Object reference is not set to an instance of an object
command.ExecuteNonQuery();
Some of the most common causes are settings, database calls or API-type calls not returning expected values. For example, you add a new field to your database and don’t populate default values for every record. The code failed to account for the fact that it queries random records, and the new field is null. KA-BOOM: Object reference not set to an instance of an object.
The null conditional operator is one of the best additions to C#. Instead of many “variable != null” checks, use “?” to return null instead of throwing an exception. This will make more sense with some examples below:
text?.ToUpper(); //from previous example, would return null
int? length = customerList?.Length; // null if customerList is null
Customer first = customerList?[0]; // null if customerList is null
int? count = customerList?[0]?.Orders?.Count(); // null if customerList, the first customer, or Orders is null
Another great feature is null coalescing, or the “??” operator. It works great for providing a default value for a null variable and works with all nullable data types.
The following code throws an exception if we don’t use null coalescing. Adding “?? new List<string>()” prevents the “Object reference not set to an instance of an object” exception.
List<string> values = null;
foreach (var value in values ?? new List<string>())
{
Console.WriteLine(value);
}
Bugs with null references happen because, in C#, any reference type object can be null at any time. What if, as a developer, you could ensure that a specific string will never be null?
What if the compiler prevented the accidental assignment of null to a variable? Sounds amazing? Good news, then: this is a real feature of the eighth version of C# called, obviously, nullable types.
The feature works ingeniously and powerfully by redefining the reference types as non-nullable by default—as many argue they should’ve been from the start.
To understand better, take a look at the following example:
static int Add(string numbers)
static int Add(string numbers)
{
return numbers.Split(“,”).Select(int.Parse).Sum();
}
In the pre-8.0 version of C#, the code above is dangerous. The numbers variable could be null, which would cause a NullReferenceException when trying to use the Split method.
The feature of nullable reference types in C# 8.0 would ensure your safety. The variable could never be null, and the call to the Split method would never throw an exception. Any attempt to pass null to the Add method would result in a compilation error.
But what if you wished to permit null values in numbers? In that case, you’d just have to add a question mark after the type’s name:
static int Add(string? numbers)
{
return numbers.Split(“,”).Select(int.Parse).Sum();
}
There has been a significant shift, and Figures can now be null too. The compiler will warn you to check the variable’s value. You can even make the warning a compiler error for extra safety.
Description: Code Snippet
The compiler indicates that “numbers” has the potential to be null. Possible solutions include:
Keep in mind that this feature is opt-in. It comes disabled by default, and you must activate it in your project’s configuration. The reason is that shipping the feature already enabled would cause breaking changes in most code bases.
C# 10 builds upon the nullable reference types feature introduced in C# 8.0, making it more robust and providing better tools for preventing null reference exceptions.
[NotNull]
and [MaybeNull]
globally in your project to make annotating types easier.Example:
// Enable nullable annotations globally for the entire project global using System; global using System.Diagnostics.CodeAnalysis; // Import the nullable attributes globally #nullable enable // Enable nullable reference types public class Example { public string? GetName() { return "John"; // This method can return null (string?) } public void PrintName() { string? name = GetName(); // Nullable reference type if (name is not null) { Console.WriteLine($"Name: {name}"); } else { Console.WriteLine("Name is null"); } } public void UseNotNullAttribute([NotNull] string value) { // This method expects a non-null value Console.WriteLine($"Value: {value}"); } public void Demo() { string? name = GetName(); UseNotNullAttribute(name); // Compiler will issue a warning } }
In this example:
#nullable enable
globally for the entire project.global using
directive to import System.Diagnostics.CodeAnalysis
, which contains the nullable attributes [NotNull]
and [MaybeNull]
, globally for the project.GetName()
method returns a nullable string (string?
), indicating that it can return null
.UseNotNullAttribute
method, we apply the [NotNull]
attribute to the value
parameter, indicating that it expects a non-null argument.Demo
method, we call UseNotNullAttribute(name)
, which passes the nullable name
variable as an argument. The compiler will issue a warning because we’re passing a nullable value to a method that expects a non-null argument, thanks to the global usings for nullable attributes.Global usings for nullable attributes help you catch potential null reference issues at compile-time, improving code safety and readability. Please note that language features and syntax may have evolved since my last update, so it’s a good idea to consult the official C# documentation for the most up-to-date information on global usings and nullable attributes in the latest C# versions.
I’ve had a motto for several years that I consistently share with my team. I refer to it as the golden rule of coding. I think every new programmer needs a tattoo that says it.
“If it can be null, it will be null”
Adding extra logic and code can help prevent null reference errors by checking if objects are null before using them. Developers should always assume everything is invalid and be very defensive in their code.
Always pretend every database call is going to fail. Typically, each domain is likely to contain highly disorganized data. Good exception-handling best practices are critical.
Null reference exceptions are common in .NET and most programming languages. Fortunately, we can all point fingers at Tony Hoare. He invented null references and even calls it the billion-dollar mistake.
What are the strategic measures we can implement to prevent this issue? One option is to adhere to my golden rule: if it can be null, it will be null!!
Nowadays, fortunately, we have the help of the compiler itself when fighting against the NullReferenceException. Turn on the “nullable reference types” feature in C# 8.0 to avoid null values if desired. The principle is: If it can’t be null, it’ll never be null. The compiler will forbid it!
Do you want to know more about C#, exceptions, and other related topics? If so, stay in tune with the Stackify blogs, since we’re always publishing posts on these topics and more.
Before you push your code, you must improve the user experience and optimize the bottlenecks. To make this possible, make sure you leverage the power of the tools at your disposal.
For example – tools from Stackify by Netreo, like Prefix. Affectionately known as the Developer’s Sidekick, Prefix is a lightweight code profiler that validates the performance of your code while you write it. As a result, Prefix helps even the most experienced developers push better code to test and receive fewer support tickets. How, you ask?
Prefix captures detailed snapshots of web requests, enabling you to quickly identify and resolve hidden exceptions, slow queries, and other performance issues. Prefix natively supports web applications in .NET, Java, PHP, Node.js, Python or Ruby. And with the recent addition of OpenTelemetry data ingestion, Prefix now handles twice the programming languages, adding C++, Erlang/Elixir, Go, Javascript, Rust and Swift. And you can download Prefix for Free and try it today!
Once in production, DevOps can ensure ongoing app performance with Retrace, our full lifecycle APM solution. With more than 1,000 customers, Retrace helps software development teams monitor, troubleshoot and optimize the performance of their applications. Retrace provides real-time APM insights, enabling developers to quickly identify, diagnose and resolve errors, crashes, slow requests and more. For more on the features of Retrace, start your Free Trial today!
If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]