If you have ever worked with C#, you are likely aware on some level of the dreaded NullReferenceException. The NullReferenceException is one of the most feared runtime errors of any application, and its undesired effect of crashing programs while simultaneously wasting developers’ time during debugging sessions is notorious. The exception is thrown at runtime when an attempt is made to use a member (e.g., property, method, etc.) or access a member variable on an object if the object that the member uses is null.
Fortunately, with the introduction of C# 8.0, you can avoid these dreaded null-related issues by taking advantage of the new feature, Nullable Reference Types, to enforce stricter null-safety rules in your code. Nullable Reference Types help ensure that you investigate your null references at compile-time, giving you an early warning system against potential null reference bugs instead of affecting them at runtime. In this article, we will look at what nullable reference types are, how they enhance safety in your code, and how you can leverage them to write better and more reliable C# code.
For reference types in C#, such as string, class, and object, assigning a null value is the default behavior. As a developer, you may appreciate your flexibility, but the concern is being affected by null-related errors if you did not perform a check for a reference type prior to utilizing it.
Before C# 8.0, C# had no way to distinguish between reference types that may or may not be initialized as null. As such, initializing objects and checking them for null before using them was left purely to the developer. C# 8.0 introduced Nullable Reference Types, which is a new way to state if a reference type is null. This is done using the? symbol to specifically designate a type to be null.
Let’s consider an example:
#nullable enable // Enable nullable reference types
public class Person
{
public string Name { get; set; } // Non-nullable
public string? Nickname { get; set; } // Nullable
}
var person = new Person();
person.Name = "John";
person.Nickname = null; // This is valid because Nickname is nullable.
Name is a non-nullable reference type (it cannot be null), while Nickname is a nullable reference type (string?), meaning it can be set to null.
The biggest advantage of nullable reference types is that they add compile-time safety checks to avoid NullReferenceException errors. If you turn on nullable reference types, the compiler will warn you if you try to assign null to a non-nullable reference type or if you try to dereference a nullable reference type without checking if it is null.
When you have nullable reference types on, the compiler checks to make sure you handle potential nulls correctly. As an example, if you try to assign null to a non-nullable reference type, you will get an error:
#nullable enable
public class Person
{
public string Name { get; set; } // Non-nullable
}
var person = new Person();
person.Name = null; // Error: Cannot assign null to non-nullable reference type.
This lets you catch a potential bug before you even run your code, which should let you make your programs less surprising and more stable.
Another benefit is avoiding accidentally dereferencing null. When you try to reference a nullable reference type without checking to see if it is null, the compiler will warn you:
#nullable enable
public class Person
{
public string? Name { get; set; }
}
var person = new Person();
Console.WriteLine(person.Name.Length); // Warning: Dereferencing a nullable value.
Name is nullable (string?) here, and getting its Length property without any null check would throw a runtime exception. The compiler warns you of this possibility before you even run the code.
Using nullable reference types makes your code more expressive and easier to read. You can instantly see for which properties or variables it is possible that they hold a null value for which makes it easier to think about the code and not include unnecessary null checks.
public class Person
{
public string Name { get; set; } // Non-nullable reference type
public string? Nickname { get; set; } // Nullable reference type
}
In the above case, it is easy to see that Name will never be null, but Nickname can be null. As a result, you can have an easier time avoiding some bugs when working with those properties.
Migrating an existing project to nullable reference types is a simple process; however, it could require some refactoring to handle existing non-nullable code with respect to the new nullability rules.
Steps to take:
First, you will want to allow nullable reference types. You can allow them globally in your (.csproj) project file or on a per-file basis with the#nullable enable directive.
Once nullable reference types have been enabled, you will probably have warnings in places where you are not handling nullability as intended. For example, assigning null to a non-nullable reference type or dereferencing a nullable type without a null check will both show warnings.
You can handle these warnings in a few different ways:
public string? GetName(Person person)
{
return person?.Name; // Safe access with null-conditional operator
}
You don’t have to change everything all at once. You can begin by enabling nullable reference types in areas where nulls might cause serious issues, and then incrementally refactor the rest of your codebase. This allows you to deploy a feature that is iterative while also safeguarding your application.
If you have some cases where you know a nullable reference type will never be null, you can use the! operator, the null-forgiving operator, to suppress the warnings.
string name = person.Name!; // Suppress warning assuming Name will never be null.
But use this carefully because it can lead to run-time errors when the assumption is wrong.
Now let’s look at some example code and see how nullable reference types work practically.
#nullable enable
public class Person
{
public string? Name { get; set; }
}
var person = new Person();
person.Name = null;
if (person.Name != null)
{
Console.WriteLine(person.Name.Length); // Safe to access Name, as null is checked first.
}
else
{
Console.WriteLine("Name is null");
}
In this example:
#nullable enable
public class Person
{
public string Name { get; set; } // Non-nullable
}
var person = new Person();
person.Name = null; // Error: Cannot assign null to a non-nullable reference type.
Here, It Name is a non-nullable reference type, so you cannot assign null to it, and the compiler will provide an error at compile time, preventing a possible null.
While nullable reference types greatly mitigate null issues, there are a few best practices to adhere to to avoid null issues:
string? name = person?.Name; // Returns null if person is null
3. Check for null Before Usage: When using nullable reference types, always check for null before using them. This will help prevent you from accidentally dereferencing a null reference.
4. Use IDisposableto Manage Unmanaged Resources. While nullable reference types are helpful,
5. Use the Null-Forgiving Operator (!Only When Necessary
If you are completely certain that the nullable reference will not be nullYou can suppress the compiler warning with the null-forgiving operator (!)
string name = person.Name!;
6. However, this circumvents the compiler’s safety functionality and should only be used when necessary. It is like saying to the compiler, “Trust me, I know what I am doing.” You still risk throwing a runtime exception if you are wrong.
7. Prefer Constructor Initialization for Required Properties
When working with non-nullable properties, initialize them through constructors to ensure they’re always set when an object is created:
public class Person {
public string Name {
get;
}
public Person(string name) {
Name = name;
}
}
8. This method enforces immutability and guarantees the required values to always available.
9. Use Annotations and Contracts for Better Clarity
When authoring APIs or libraries, adding attributes such as [NotNull], [MaybeNull], or XML documentation can make your intent clearer and enhance tool support.
Nullable reference types in C# represent more than a language feature — they are a change in philosophy towards safer, more defensive, and more intentional programming. If you enable this feature, you will:
Simply put, nullable reference types are a way to get the compiler to do more of the heavy lifting for you, to allow you to write applications that, before this point, are robust and resilient
If you are building new applications or maintaining high-value production applications, enabling nullable reference types is one of the easiest and most powerful mechanisms for quick-better-code-now!
To recap:
<Nullable>enable</Nullable>
In today’s .NET world — with C# powering everything from web applications to cloud computing and cross-platform mobile apps — null-safe code is more than a best practice: it’s required.
Nullable reference types add compile-time safety to one of the most dangerous areas of programming. It might feel like a small step forward, but the impact this can have on the quality of your code cannot be understated. So next time you have a ? in your type declaration, consider it not a pain in the butt, but rather as a guardian-keeping your code safe from the sneaky bugs that go undetected… until your app goes down on production time.
Why You Should Care About C#’s ‘Nullable Reference Types’ was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.
Also read: Tesla promet de révolutionner le monde et l’humanité avec son nouveau plan