Exciting New Features Coming in C# 8 – Nullable Reference Types

For software developer’s in a continuous cycle of projects and deadlines, it’s easy to forget to take a moment each quarter to stop and see what new features or changes in software development technology are on the distant horizon.  This November marked the 17th year since C# first appeared into stable relexase and since then it has continued to evolve becoming one of the world’s most popular programming languages. C# is currently in the 7th major iteration and will remain so for much of 2018, however developers are already talking about some of the exciting new features coming in C# 8. Having knowledge of upcoming features early can give a developer time to think about more ways those new features can be used later as they develop without them today. With that in mind, let’s look at one of the upcoming features in C# release 8 called Nullable References Types.

In researching this, I learned that the concept of a null reference was invented over 50 years by British computer scientist Sir Tony Hoare. The ability to null references in programming languages has been debated for some time as it has at been at the root of more software bugs than any other in the history of computer programming. So much so, that Sir Tony Hoare famously apologized for its creation at a 2009 conference calling it a “billion-dollar mistake” that “has led to innumerable errors, vulnerabilities, and system crashes.”

Given the current evolutionary state of most programming languages, it’s pretty much impossible to simply do away with the null reference. Changes to a programming language must to be done in a compatible manner, so even if such a change was possible, it can’t be done because it would break most existing code by making it impossible to compile.

One of the primary causes of bugs that will null reference exceptions in a language like C# is that there is no way clearly express to the compiler if a variable, property, or field should or can be null. Note that I said compiler and not developer. Attempts to tell the developer through code comments or reference documentation isn’t sufficient as it not guaranteed, time consuming, and prone to human error. Having the compiler know if a variable may be null will allow its analysis to definitively warn the developer of a problem and enforce against it.

The Nullable Reference Types features attacks this problems in two ways. First by taking a prior added language feature of nullable value types and turns it into a definite way of expressing intent. Second, it enforces the behavior in the compiler in a way that pushes the edge in terms of compatibility with existing code in that while all early existing code will continue to compile and function, it will come with many compiler warnings not previously seen before.

Expressing Intent

One prior feature added to C# added the ability to make value types nullable by marking the value type with a question mark at the end. This addition of this feature wasn’t simply done to just for the sake of allowing a null value type, but as a planned transition to slowly steer development towards Nullable Reference Types. With C# 8, the placing of a question mark character on ANY type will now be treated as an indicator of weather a variable may or may not be null, not in terms of preventing compilation outright, but in terms of how the compiler responds and warns about it.

For example, consider this simple class that holds an address:

public class Address

{

public string Address1;

public string? Address2;

public string City;

public string State;

public string ZipCode;

}

 

With C# 8 The compiler will now interpret the intent to be that all fields should never be null except for the Address2 field.

Enforcing Behavior

To make the coded expression of intent meaningful, the compiler must enforce it to some degree while at the same time not completely breaking the compiling of existing code. For this feature, any issues found by the compiler in terms of variable usage and nulls will be identified as a warning. Warnings will not stop the compiling of code nor force any changes to be made. Some systems the automatically compile code, may treat warnings as an error and will have to be adjusted for this. For cases where warnings are not desired, an option to disable the nullable reference warnings will be added.

It’s important to note that there is no guaranteed null safety nor any change to how code is generated or behaves. The purpose of this feature is to help identify issues, not impose new constraints on how code is written. There can still be ways around what the compiler can detect, and so developers should continue best practices and be aware of how null is used.

The compiler tracks the null state of a variable through the code and issues warnings accordingly. Below is an example code block with comments for the error generated, if any.

 

 

void SomeMethod(string? data)

{

OutputData(data.Length);                    // WARNING: may be null

 

if (data != null)

{

OutputData(data.Length);                // No warning… is not null here

}

 

if (data == null)

{

return;                                        // No warning…not null after this

}

 

OutputData(data.Length);                    // No warning, can’t be null  here

 

data = null;

OutputData(data.Length);                    // WARNING: may be null

}

 

In the above case, the “data” parameter may be null. The first call to the OutputData method generates a warning because it is uses the Length field without checking if the data string is null. The second call to OutputData does not generate a warning because compiler sees a condition check for null was made on the data parameter. The third call to OutputData doesn’t generate a warning because the prior condition check for null would leave the method. For the last call to OutputData, a compiler warning occurs because data was explicitly set as null in a prior statement.

 

False Negatives and Positives

While the ability of the compiler to track variable states, it is not infallible nor can it keep track of variables outside the method scope.

Below is an example of a false negative in that something is happening that could potentially set a variable null but the compiler doesn’t see it.

void SomeMethod(Address address)

{

if (address.Address2 != null)

{

address.ResetAllFields();                      // Compiler can’t see this…

OutputData(address.Address2.Length);   // So no warning here

}

}

 

In this case the call to the ResetAllFields may set fields in the address object to null. If the Address2 field should happen to become null then the OutputData that follows will cause an exception. The compiler doesn’t see what happens in the ResetAllFields, only that there was an earlier null condition check that passed. Its important that developers continue to be aware of what occurs in term null usage and not rely solely on the compiler to point everything out.

There is the possibility of false positives, when the compiler makes a warning that it shouldn’t, usually because some condition will result in the variable never being null, but the compiler doesn’t know it. False warnings can be a nuisance and so to prevent having to sift through false warnings, C# 8 will also allow the use of an exclamation character after a variable name to tell the compiler to not worry about null in this expression.

void SomeMethod(Address address)

{

OutputData(address.Address2!.Length);   // No warning as ! means don’t worry abou it

}

 

In the above code, the exclamation mark after Address2 tell the compiler to not warn about the possibility of Address2 being null as you, the coder, has make sure it won’t happen.

Non-nullable References

We’ve covered cases for variables that can be null, now let’s look at the other side with variables that should never be null.

This is where existing coding will likely generate the most warnings, primarily for the case of uninitialized variables. In the case of the Address class above warning will occur on the commented lines below:

public class Address

{

public string Address1;             // WARNING: uninitialized

public string? Address2;

public string City;                        // WARNING: uninitialized

public string State;                      // WARNING: uninitialized

public string ZipCode;               // WARNING: uninitialized

}

 

Other cases are noted in the code below:

void SomeMethod(Address address)

{

address.Address1 = null;                         // WARNING: Shouldn’t be null

address.City = address.Address2;  // WARNING: May be null

string s = default(string);                         // WARNING: Shouldn’t be null

}

 

It’s important to note that there are two commonly used null cases that the compiler will not be generating a warning for because they are too commonly occurring or simply unavoidable. The first case is with fields in a struct like below:

struct Container

{

public string Name;                // No warning…too common and too breaking

}

 

This will not generate a warning as a struct requires a default public constructor that can be instantiated in numerous ways to create an empty instance. Having a warning for this case is impossible to work around.

The second case that is ignored is array creation:

void SomeMethod()

{

string[] a = new string[10];      // No warning…too common and unavoidable

}

 

In the above case, the contents of the array should not be null, but the creation of a new array must still be possible prior to populating it.

Conclusion

This feature will likely cause some minor transition pains with existing code, but the benefits are worth it in moving towards coding in a more “null aware” manner. You might be surprised to find bugs you never knew existed that would cost you time and money down the road.

To find out more about upcoming changes to C# visit the official C# design repository at https://github.com/dotnet/csharplang

 

Share this post

Related Posts

Webinar Recap: CMMC News for the DIB

The DoD announcement on Nov 4 “Strategic Direction for the CMMC Program” provided new guidelines for CMMC 2.0. Our KTL webinar on Nov 10 helped to clarify these changes and answer questions for those in the DIB.

Read More »