A .NET guideline specifies that an application should never throw a
NullReferenceException. However, many applications and libraries do. The
NullReferenceException is the most common exception happening. That’s why C# 8 tries to get rid of it. With C# 8, reference types are not null be default. This is a big change, and a great feature. However, what about all the legacy code? Can old libraries be used with C# 8 applications, and can C# 7 applications make use of C# 8 libraries?
This article demonstrates how C# 8 allows mixing old and new assemblies.
Why avoiding NullReferenceException?
NullReferenceException occurs, the reason often is not easy to find. Errors occur at places far away from the real issue. That’s why applications should not throw
NullReferenceException, and instead check for null values, and throw a
ArgumentNullException. If a null value is passed with an argument, at method entry it can be checked to not accept the null value. Throwing an
ArgumentNullException here, it’s easy to detect where the problem is.
See the guidelines on throwing standard exception types
Let’s see what C# 8 does to avoid the
Install C# 8
At the time of this writing, C# 8 is not yet released. However, you can try it out. At the time of this writing, to try nullable reference types, Visual Studio 2017 15.5-15.7 is needed – and the preview of C# Nullable Reference Types. You can easily install it, and uninstall it as described in the linked article.
Installing this version of the compiler, you’ll get many warnings on many of your existing C# projects. By default, the latest major version of the C# compiler is used. To get rid of the warnings, you can explicitly set the C# compiler version to an earlier version with your existing projects, or you can also uninstall the C# 8 compiler again.
Reference Types are not Nullable
The new nullability is easy to understand. The syntax is similar to nullable value types. Similar to value types, if the ? is not specified with the declaration of the type, null is not allowed:
While the syntax with value types and reference types now looks similar, the functionality behind the scenes is very different.
- With value types, the C# compiler makes use of the type
Nullable. This type is a value type and adds a Boolean field to define if the value type is null or not null.
- With reference types, the C# compiler adds the Nullable attribute. Version 8 of the compiler knows about this attribute and behaves accordingly. C# 7 and older versions of C# don’t know about this attribute, and just ignore it.
Compiling a program with C# 8, both
Book bwith C# 7.
Book class defines the non-nullable properties
Publisher, and the nullable property
Isbn. In addition to that, this type contains a constructor making use of C# 7 tuples and deconstruction. Using the
Book type and accessing the
Isbn property, a value can only be written to a variable of type
string?. Assigning it to
string results in the C# compilation warning converting null literal or possible null value to non-nullable type.
Assigning Nullable to Non-Nullable
In case you need to assign a nullable type (like the
Isbn property from the
Book class), C# 8 analyzes the code. In the code snippet, because the
isbn is compared to null, after the
isbn cannot be null anymore. After the
if statement it’s ok to return the
isbn variable of type
string? although the method is declared to return the non-nullable
Of course, you can also use the coalescing operator instead. This also allows to use a simple Lambda with the implementation of the method:
Returning from and passing to methods
The class NewAndGlory is defined in a class library built with C# 8. The method
GetANullString is defined to return a type
string?, so null is allowed, and this method just returns null. The method GetAString is defined to return a type of
string, so null is not allowed. With the method
PassAString, the parameter is defined to receive
string. Here, null is not allowed, and there’s no need to verify this.
On the other hand, there’s the library TheOldLib, which makes use of the C# 7.0 compiler. This is defined in the project file
TheOldLib.csproj with the element
Legacy class defines the method
GetANullString that just returns null, and the method
PassAString that receives a string and does the usual testing for null before the string is used. This library also defines the interface
ILegacyInterface, which defines a method that returns a string. This string could be meant to be nullable or not, with C# 7 this cannot be specified in the interface.
C# 8 Application using Libraries built with C# 7 and C# 8
Now let’s get into the C# 8 Console application that references both the old and the new libraries. Using the class
NewAndGlory, as excpected the result from the method
GetNullString can only be written to a
string? type. Trying to pass null to the method
PassAString retsults in the the compilation error cannot convert null literal to non-nullable reference or unconstrained type parameter.
Legacy class where the method
GetANullString returns null, the result can be written in a
string type. Because this library is not implemented with C# 8, the C# 8 compiler doesn’t create a warning. This only happens with new libraries. It’s also possible to invoke the method
PassAString and passing null. Using legacy libraries too many errors would be shown, that’s why libraries not built with the new compiler are dealt with differently.
Foo of the interface
ILegacyInterface defined in the library built with the C# 7 compiler returns a
string. How can this be implemented with C# 8? As you can see in the following code snippet, the interface can be implemented in both ways – returning a nullable string, or a non-nullable string. This is a good way, as with C# 7 it was not possible to declare how this was meant.
Interfaces declared with the C# 8 compiler do need the correct implementation in regard to nullability.
C# 7 Application using Libraries built with C# 8
From an old application (an application using C# 7 or earlier), the new C# 8 built library can be used like any other .NET library. The new app doesn’t see nullable types such as
string?, and sees
string instead – which is nullable for C# 7 anyway. The non-nullable
string type becomes a nullable
string type. This way, the old application can make use of the new library. Of course, now the new library has the same issues in regard to nullability as the old library:
PassAStringMethod and passing null generates a
NullReferenceException, when the
ToUpper method is invoked on the string. This is the exception we want to avoid, but it still occurs in such an interop scenario. To avoid this, in the C# 8 library we can still check for null and throw a
ArgumentNullException which is not required with C# 8 clients. Maybe the next version of the C# 8 compiler automatically creates this implementation with non-nullable types for older C# clients – but it’s only needed for the time when older than C# 8 compilers are used.
Non-nullable reference types is a new C# functionality that will get rid of many of the
NullReferenceException exceptions. This is made possible by changing the default behavior of reference types. Although the default behavior changes, a new C# 8 application can still use old libraries, and a old C# application can make use of new C# 8 libraries. Nullability is implemented by using attributes, which makes this possible.
What do you think about this new C# 8 feature?
Get the complete sample from More Samples!
Before C# 8 is released, read about all the cool C# 7 features in the book Professional C# 7 and .NET Core 2.0!
Enjoy programming and learning,