This blog post is part of series of latest C# 8.0 features. In this post we will explore in detail about the new feature Nullable reference types.
Well you read it right! Till now reference types were by default nullable i.e. they can hold null value. But that increases the chances for null reference exceptions. Not every time developers handled it correctly or it could be some other assembly or API not behaving correctly which causing your code to throw an exception. With C# 8.0 nullable reference types compiler will warn you about unsafe behavior of code and possible occurrences of any possible dereferences of null reference. You can only assign null to variable if it is nullable.
Lets look at the below code snippet. Compiler is not showing any warnings or errors. But if you try to run the application it will throw null reference exception. In this scenario it is straightforward but in real life you might be receiving data from other assembly or API that could return you bad data you might not have considered.
using static
System.Console;
namespace NullableStringTypes
{
class Program
{
static void Main(string[] args)
{
Customer customer = null;
if(customer.MiddleName.Length > 0)
{
WriteLine($"Customer Name: {customer.FirstName} {customer.MiddleName} {customer.LastName}");
}
else
{
WriteLine($"Customer Name: {customer.FirstName} {customer.LastName}");
}
ReadLine();
ReadLine();
}
}
class Customer
{
public string
FirstName { get; set; }
public string
MiddleName { get; set; }
public string LastName
{ get; set; }
}
}
In preview version nullable reference type is not enabled by default, in released version it will be by default enabled. So you need to enable it explicitly by adding below line to .csproj file after LanguageVersion settings.
<NullableReferenceTypes>true</NullableReferenceTypes>
Once the feature is enabled, compiler is showing five warnings, it also highlight the code and suggest fixes.
The first warning says "Converting null literal or possible null value to non-nullable type." the compiler is complaining about assigning null to Customer object. We have been assigning null to reference types so many years. Which is no longer the case, you cant assign null to the reference type. But then how to get rid of this warnings? You can assign it to real object. So create an instance of customer object and assign it to variable. First two warnings went away. But there are still three more warnings, compiler is screaming that properties of Customer class are not initialized hence they will be initialized be default to null and are non-nullable. So I will go ahead and create constructor and assign string.empty to it. The compiler warnings will go away and now we have clean code.
public Customer(string firstName, string lastName, string middleName)
{
FirstName = firstName;
LastName = lastName;
MiddleName = middleName;
}
But wait... what if you want to specifically assign null, then you need to make it nullable like you do with value types.
In above scenario the customers middle name can be null. So we can make it nullable like specified in below code snippet.
public Customer(string firstName, string lastName, string middleName = null)
{
FirstName = firstName;
LastName = lastName;
MiddleName = middleName;
}
Customer customer = new Customer("John", "Snow");
Now the compiler is displaying warning for below line, since we are not handling the possible null reference conditions.
if(customer.MiddleName.Length > 0) // Compiler warns about possible dereference of // null reference
To resolve the warning you can use null coalescing operator (?) as defined below.
if(customer.MiddleName?.Length > 0)
Now we have get rid of all the warnings, now if I try running the application. Voila! it runs like a charm without any exceptions.
The compiler parse the code and try all possible combinations and track down any possible deference of null references and warn you. Since it annotates and highlights the occurrences it is hard to ignore.
So can we achieve true null-safe code? Not really, while adding this convenience, compiler can not track down all the scenarios. There are still some loopholes which you need to consider.
1. If your code is calling a method from assembly compiled with older version or feature is set to off. Then compiler can not determine the intent (called as "null-oblivious") and can not warn and just pass the code.
2. Some of the scenarios are hard to handle so are not covered by this feature. For example in below code snippet I have created array of Customer objects. When array is initialized it is set to null as compiler can not keep track of initialization of all array elements. With below code snippet if you run the code, the code will throw null reference exception and compiler didn't warned you about it.
Customer[] customers = new Customer[10];
WriteLine($"Customers List:");
for (int i = 0; i
< 10; i++)
{
if (customers[i].MiddleName?.Length > 0)
{
WriteLine($"{i.ToString()}. {customers[i].FirstName} {customers[i].MiddleName} {customers[i].LastName}");
}
else
{
WriteLine($"{i.ToString()}. {customers[i].FirstName} {customers[i].LastName}");
}
}
All in all, the feature provides better safeguarding against null-reference exceptions. Upgrading existing code can be hell of a job, to ease on that you can use trick to enable this feature locally by using compiler directive #nullable enable and #nullable disable. This will help you to update code gradually, piece by piece.
Comments
Post a Comment