To Null or Not Null

Recently after posting my article C# 6 – Null-Conditional Operator an interesting discussion was going on at Reddit. While the overall feedback was very positive, a few argued to never use null.
I’m in the same camp as David Fowler with Software is about making tradeoffs.. You could be strict about always using the null-conditional operator, strict about not using it, or in between to use it when it makes sense. While the previous post explained, the null-conditional operator, in this article I want to discuss null (or Nothing in Visual Basic) in a more general term, and also show what’s in the pipeline for future versions of C#.

Null or Nothing

NullReferenceException

If you are not a beginner programming .NET, I’m sure you already had your experiences finding issues of NullReferenceExceptions. If the exception was not in your code, the reason to find the issue is harder to find. There’s a .NET guideline that your code should never throw exceptions of type NullReferenceException. One way to deal with this is to allow null. This way you need to deal with this behavior accordingly. However, if the method cannot do anything useful without the received object, it should throw ArgumentNullException.

The following code snippet throws an ArgumentNullException if the received Bar object is null. In case you need the object within the method (the Foo method in the sample), throw an exception if the method is invoked in a wrong way. This helps the caller immediately when the method is invoked. Otherwise, the exception comes from a method that makes use of the Bar object, such as the Doit method:

public void Foo(Bar b)
{
    if (b == null) throw new ArgumentNullException(nameof(b));
    Doit(b);
}

The nameof keyword available with C# 6 helps dealing with such exceptions. Instead of passing a string, you take the name of the variable. This helps a lot with refactoring.

Often I’m asked why it’s better to throw an ArgumentNullException instead of a NullReferenceException – the program throws in both cases. If you let the program throw a NullReferenceException, the exception can be thrown any hierarchy deep in the stack trace. Often it’s not that easy to find the reason. As soon as you are not a newbie, I’m sure you already had to deal with such issues. Throwing an ArgumentNullException you immediately see the issue when calling the method where you passed an incorrect initalized instance. The error is a lot easier to find.

Allowing null

There are some scenarios where null is a useful value. There’s also a reason database columns can be specified nullable, where other columns require a value. Receiving JSON or XML from a service, often some values are optional. Many methods of the .NET Framework return null in case nothing was found. Let’s get at one example. Part of the example is taken from the Language Integrated Query chapter of my book Professional C# 6 and .NET Core 1.0

The following code snippet throws an exception of type InvalidOperationException because the predicate passed to the First method doesn’t return any racer. The sequence contains no matching element. Indeed, the Formula1.GetChampions method returns a list of all Formula 1 world champions. As of today, there’s no champion from Hungary. Hungary has a Formula 1 race, and there was one Formula 1 race driver from Hungary (Zsolt Baumgartner), but his best race position was 8th at the United States GP in the year 2004.

var racers = Formula1.GetChampions();
Racer hungarianRacer = champions.First(r => r.Country == "Hungary");

It’s not a good idea to throw exceptions for complete normal circumstances. If you require a result with your query it’s ok to throw an exception. However, if this could be normal behavior it’s better to use the FirstOrDerfault method instead of the First method. FirstOrDefault doesn’t throw an exception, but returns the default value for a type in case no matching element was found. With a reference type, the default is null.

var racers = Formula1.GetChampions();
Racer hungarianRacer = champions.FirstOrDefault(r => r.Country == "Hungary");

Before using hungarianRacer you need to check for null.

As the hungarianRacer variable indeed is null, I’m doing a query for UK racers next. The next result should be the number of race wins from the first UK Formula 1 champion. The Racer class is defined with a Starts property that can never be null, and a Wins property that can be null. This is a simplified example, and of course the Wins property could be defined to be not nullable as well. However, in a usual entity type you often find properties, and often properties accessing relations to be of reference types, and thus null is a valid value. With the Racer class, the Country property could be null as well.

public class Racer
{
    // more members
    public string Country { get; set; }
    public int Starts { get; set; }
    public int? Wins { get; set; }
}

A traditional way to deal with this verifying if the received object is not null, such as checking ukRacer, and then to check if the Wins property is not null before accessing the value:

Racer ukRacer = racers.FirstOrDefault(r => r.Country == "UK");
int wins = 0;
if (ukRacer != null)
{
  if (ukRacer.Wins != null)
  {
    wins = ukRacer.Wins.Value;
  }
}

With code I’m reviewing I often see such examples where not only two if statements are nested. With the previous example it would be possible to combine the two if statements into one – using a logical operator.
Using the C# 6 null-conditional operator, the code can be simplified. If ukRacer is null, the result of the expression is null, and thus because of the coalescing operator, 0 is assigned to the wins variable. If ukRacer is not null, the value of the Wins property is accessed. If the value of the Wins property is null again, the coalescing operator comes into play. If the value is not null, the value is assigned to the wins variable:

Racer ukRacer = racers.FirstOrDefault(r => r.Country == "UK");
int wins = ukRacer?.Wins ?? 0;

Of course this can also be simplified into a single statement:

int wins = racers.FirstOrDefault(r => r.Country == "UK")?.Wins ?? 0;

Using the as Operator

It’s best to never do a cast. However, often this is not possible – at least not without a lot of work. Using the as operator, the result needs to be verified if it is not null:

var r = p as Racer;
if (r != null)
{
    int wins = r.Wins ?? 0;
}

The null-conditional operator becomes very handy:

int wins = (p as Racer)?.Wins ?? 0;

Event Handling

I’ve already shown the event handling sample with the null-conditional operator in a previous blog post, but because it is such a useful C# 6 enhancement dealing with null, I’m repeating int here:

Old code:

public event EventHandler<CarInfoEventArgs> NewCarInfo;
 
private void OnNewCarInfo(string car)
{
  EventHandler<CarInfoEventArgs> handler = NewCarInfo;
  if (handler != null)
  {
    handler(this, new CarInfoEventArgs(car));
  }
}

New code:

public event EventHandler<CarInfoEventArgs> NewCarInfo;
 
private void OnNewCarInfo(string car)
{
  NewCarInfo?.Invoke(this, new CarInfoEventArgs(car));
}

Nullable Reference Types and Nullability Checking

While it is ok with some members to be null (such as you’ve seen), the values of most reference types should never be null. If C# would be created today, it would not be allowed to assign null to a reference type, unless it is explicitly allowed. With today’s C# implementation, this feature is available with value types. Value types couldn’t be null since the first version of C#. The creation of Nullable allowed value types to be null. Nullable is a generic type that allows using any value type as the generic parameter, and thus allow assign null to it. Behind the scenes, Nullable defines a boolean field which defines if the value is null or not. Thus a nullable value doesn’t have the overhead of a reference type.
As Nullable is such an important feature, C# allows adding ? to the type where the compiler in turn defines a type of Nullable instead.

int x1 = 3;
Nullable<int> x2 = null;
int? x3 = null;

Point p1 = new Point(10, 20);
Point? p2 = null;

Nullable reference types and nullability checking will not be part of the upcoming C# 7, but there’s a good chance for this feature in versions available after the release of C# 7.

This nullability is already in plans for a future version of C#. With the current plans, all the reference types cannot be null – by default – unless the ? is added to the type, such as shown in the following code snippet. With a declaration of string? s2, the variable s1 is allowed to be null. This is not allowed with the declaration string s1; Using the s1 variable, null cannot be assigned to.

string s1 = &quot;one&quot;;
string? s2 = null;

s1 = s2; // compiler warning

int length1 = s1.Length; // ok
int length2 = s2.Length; // compiler warning
int? length3 = s2?.Length; // ok

With generics, the type T now defines types that are not nullable, whereas T? allows nullable types.

Changing the default behavior of course has a big impact of existing libraries. It’s not possible to change all the existing libraries that should be used. That’s why, there’s an opt-in and opt-out mechanism planned using global attributes. Using existing libraries that do not have the new attributes, no compiler warnings will happen, and thus using these libraries works the same as it is today. Re-building a library taking care of the new behavior, an assembly attribute is applied to mark the assembly using the new behavior with nullable reference types. In case it’s too much work to modify the assembly for the new behavior, it’s also possible to opt out using the new compiler.

More information about this proposal is available on GitHub: Nullable reference types and nullability checking.

I can’t wait to try this proposal with some existing code, but it will take some time to have this feature. For sure this feature will not be part of C# 7. With a future version of C#, when this might be implemented, the feature might look different than shown in the proposal. For sure this will be a big change, and having it used with many libraries, it’s easier to deal with null issues.

Summary

Some variables of reference types should never be null, and it’s completely ok with others to be null. Just use it one way or the other where it makes sense. Dealing with null, using the null-conditional operator code becomes less messy and you can have a better focus on the functionality itself. Of course, the null-conditional operator should not be used in every case. Throwing ArgumentNullException is still an exception that needs to be dealt with.
The C# vfuture proposal on nullable reference types and nullability checking looks very promising, but there’s still some time to come before we can use this.

Have fun with programming and learning!
Christian

More Information

More information about the C# features available with C# 6 in my new book Professional C# 6 and .NET Core 1.0. In my workshops you can already get some information about upcoming features with C# 7 – if you like to – or focus on already shipping features.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.