Author avatar

Dániel Szabó

Nullable Types and Their Consumption in Csharp

Dániel Szabó

  • Nov 26, 2019
  • 9 Min read
  • 5 Views
  • Nov 26, 2019
  • 9 Min read
  • 5 Views
Languages Frameworks and Tools
C#

Introduction

Nullable types were introduced with version 7.0 of C#. These types represent instances of the System.Nullable<T> class. They are the values of an underlying type T and an additional null value. The T can be any non-nullable value type, but it cannot be a reference type. This guide will introduce you to this relatively new feature and show you what kinds of problems it intends to solve. We will see practical examples that show how this new feature can fix most of your null-related bugs before they crash your application.

The Billion-Dollar Mistake

In 1965 a man named Tony Hoare, one of the giants of computer science, invented the null reference. At that time, he was designing the first type system for references in an object-oriented language called ALGOLW. His goal was to ensure the use of all references would be perfectly safe, and this was ensured by checks from the compiler. This is where he put the null reference, and the reason was simple: it was easy to implement. This led to countless errors, vulnerabilities ,and system crashes. It is assumed to have caused billions of dollars of damage in the last fifty or so years.

This is why the null reference has been called "the billion-dollar mistake."

Nullable Types

The problem is how easy it is to use null references. These are the default value in C# for every reference type. The dilemma is answered with these questions:

  • What else could the default value be?
  • What other value would be most fitting until you decide? -What other value could be used to initialize an array or references until it is filled with actual values?

The problem—or at least part of it—comes from the fact that C# does not let you express whether a null as a value in a specific context is good or bad.

What can you do?

  1. Express intent
  2. Enforce behaviour
  3. Avoid dereferencing nulls
  4. Avoid nulls

We are using nullable types when we need to represent an undefined value of an underlying type. While Boolean values can have either true or false values, a null in this case means false as there is no undefined value. When you have a database interaction, a variable value can be either undefined or missing. A field in a database may contain true, false, or no value at all. In this case, a nullable of type bool is the way to go.

Some things to keep in mind as you work with nullable types:

  1. This type represents value-type variables that can be assigned the null value.
  2. The T? is the shorthand syntax for Nullable< T >.
  3. Assign a value to a nullable type as you would with other types int? = null or double? = 10.0.
  4. HasValue and Value readonly properties can be used to check and get the value from such a type.
  5. == and != operators can be used with a nullable type.
  6. You can use pattern matching since version 7.0 of C#.
  7. Default value for T? is an instance where HasValue evaluates to false.
  8. The ?? operator—null-coalescing operator—allows you to assign a value to the underlying type based on the value of the nullable type.

Let's declare some nullable types.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;

namespace nllables
{   
    class Program
    {
        static void Main(string[] args)
            {
            double? nullableDouble = 9.99;
            int? nullableInt = null;
            double standardDouble = 19.19;
            Console.WriteLine($"The variable :: {nameof(nullableDouble)} has value :: {nullableDouble} and HasValue evaluates to :: {nullableDouble.HasValue}.");
            if(nullableInt.HasValue)
                { Console.WriteLine($"The variable :: {nameof(nullableInt)} has value :: {nullableInt.Value} and HasValue evaluates to :: {nullableInt.HasValue}"); }
            else
                { Console.WriteLine($"The variable :: {nameof(nullableInt)} has no value!"); }
            Console.WriteLine($"The variable :: {nameof(standardDouble)} has value :: {standardDouble}.");
            Console.ReadKey();
            }
    }
}
csharp

This gives us the following output:

1
2
3
The variable :: nullableDouble has value :: 9.99 and HasValue evaluates to :: True.
The variable :: nullableInt has no value!
The variable :: standardDouble has value :: 19.19.
bash

In this demo, we'll use shorthand syntax to declare two nullable types, one double and one int. We use the HasValue in the if block to decide if the value for the int is right or not.

If we try to refer to the int without the if block, we see the following exception:

1
System.InvalidOperationException: 'Nullable object must have a value.'
csharp

This exception pops up at compile time. Let's fix the issue before it hits production.

Let's convert a nullable type to an underlying type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;

namespace nllables
{   
    class Program
    {
        static void Main(string[] args)
            {
            double? d = null;
            int? c = 9;

            int g = c ?? 99;
            double f = d ?? 99.9;

            Console.WriteLine("The nullables...");
            if (d.HasValue)
                { Console.WriteLine($"The variable: {nameof(d)} has value of {d.Value}"); }
            else
                { Console.WriteLine($"The variable: {nameof(d)} has is null!"); }

            if (c.HasValue)
            { Console.WriteLine($"The variable: {nameof(c)} has value of {c.Value}"); }
            else
            { Console.WriteLine($"The variable: {nameof(c)} has is null!"); }

            Console.WriteLine("The converted...");
            Console.WriteLine($"The variable: {nameof(g)} has value of {g}");
            Console.WriteLine($"The variable: {nameof(f)} has value of {f}");

            Console.ReadKey();
            }
    }
}
csharp

The output is as follows:

1
2
3
4
5
6
The nullables...
The variable: d has is null!
The variable: c has value of 9
The converted...
The variable: g has value of 9
The variable: f has value of 99.9
bash

We have two nullable type variables, d and c. The null-coalescing operator allows us to convert the variables as far as their values are concerned to standard variable types g and f.

Our conversion may need some explanation. The int g = c ?? 99; line works behind the scenes as follows. It checks if the c is a nullable type and calls the HasValue operator If the return value is true it is assigned to g; otherwise 99** is assigned. The same thing happens indouble f = d ?? 99.9;which shows another case, where the value isnullfor the variable so99.9is assigned as the value to thef` variable. Keep in mind that these values need to be implicitly convertible as the value is concerned.

As a simple example shows,the following action will die with an exception:

1
int g = c ?? 99.9;
csharp

The error states:

1
2
CS0266	Cannot implicitly convert type 'double' to 'int'.
An explicit conversion exists (are you missing a cast?)
bash

What about the operators?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;

namespace nllables
{   
    class Program
    {
        static void Main(string[] args)
            {
            double? a = null;
            double? b = 99.9;

            bool? c = true;
            bool? d = false;
            bool? e = null;

            int? f = 10;
            int? g = null;

            Console.WriteLine("Do the math...");
            Console.WriteLine($"a + b = {(a + b)}");
            Console.WriteLine($"a * b = {a * b}");
            Console.WriteLine($"a * f = {a * f}");
            Console.WriteLine($"b * f = {b * f}");
            Console.WriteLine("The booleans...");
            Console.WriteLine($"c | d = {c | d}");
            Console.WriteLine($"c | e = {c | e}");
            Console.WriteLine($"d & e = {d & e}");
            Console.WriteLine($"c & e = {c | e}");

            Console.ReadKey();
            }
    }
}
csharp

The result:,

1
2
3
4
c | d = True
c | e = True
d & e = False
c & e = True
bash

From this experiment, we conclude that any operation in the field of mathematics produces nothing when it is involved with a null on either side. As for the Boolean part, the situation is a bit more complicated because the c & e = True shows that true & null results in True while false & null evaluates to False. This article further discusses nullable Boolean logical operators if you are interested.

Conclusion

This guide took us on a journey to the realm of nullable types. After a brief introduction and history you learned how to wield this new feature of C#. I hope this has beed informative to you and that you have found what you were looking for. If you liked this guide give it a thumbs up!

0