DEV Community

Sahan
Sahan

Posted on • Originally published at sahansera.dev on

Understanding Nullable Value Types in C#

nullable values ide

I recently started picking out different topics that I like to learn more about. One such thing I wanted to have a closer look at is “Nullable value types in C#“.

We first need to understand the ‘why’ behind nullable value types.

Let’s have a look at this class:

public class User
{
    public string Name { get; set; }
    public int DaysSinceLastLogin { get; set; }
    public DateTime RegistrationTimestamp { get; set; }

    public User()
    {
        DaysSinceLastLogin = -1; // Magic Number
        RegistrationTimestamp = DateTime.MinValue; // Magic Number
    }
}
Enter fullscreen mode Exit fullscreen mode

When designing a class like this, we often come across situations where we want to default a value type to a null or tell the program that there’s no actual value set.

In the above example, since RegistrationTimestamp and DaysSinceLastLogin are value types, we can’t represent that an actual value exists, we need to default them to the magic numbers we chose. Two major drawbacks of this approach are, we wouldn’t know what the actual magic numbers represent and if we decide to change the value we need to do some plumbing to get around it.

For a much cleaner implementation we can make use of C#‘s out of the box Nullable Value Types.

Definition of the Nullable Value Types

The MSDN definition goes as follows:

A nullable value type T? represents all values of its underlying value type T and additional null value.

Let’s think of DaysSinceLastLogin from the above example. Its type is Int32 which spans from -2,147,483,648 to 2,147,483,647. Obviously, in our context, we don’t need negative numbers to represent how many days have elapsed since the last login of the user (we could have used uint instead, but let’s not get bogged down with such details). For the sake of the argument, let’s look at how to can convert it to a Nullable value Type.

nullable diagram

What this does is, essentially, it wraps our value type of int in a Nullable struct; which allows us to assign it an integer value or a null

Refactoring our code with Magic Numbers

public class User
{
    public string Name { get; set; }
    public Nullable<int> DaysSinceLastLogin { get; set; }
    public Nullable<DateTime> RegistrationTimestamp { get; set; }

    public User()
    {
        DaysSinceLastLogin = null;
        RegistrationTimestamp = null;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now we can simply assign nulls to our DaysSinceLastLogin and RegistrationTimestamp. But, C# is known for its nice syntactic sugar. Let’s sugarcoat it like so:

public class User
{
    public string Name { get; set; }
    public int? DaysSinceLastLogin { get; set; }
    public DateTime? RegistrationTimestamp { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

This is beautiful, and behind the scenes, it still wraps in a Nullable<T> struct. Another good thing is that now we don’t need to explicitly specify null to DaysSinceLastLogin and RegistrationTimestamp. We all know that nulls are a billion dollar mistake, but still, could have been worse 😁

Convenience properties and methods of Nullable

  • .HasValue - true if it has a value; false if null
  • .Value - Gives us the underlying value. However, this will throw an InvalidOperationException if you try to access a value whose .Value property is false
  • .GetValueOrDefault() - Underlying value or default. Eg: for an integer whose value has not been set, will return 0-.GetValueOrDefault(T) - Value or a specified default value.

In my next post, we will look at how we can access and check for null values in C#. Until then ✌️

References

  1. https://docs.microsoft.com/en-us/dotnet/api/system.nullable-1?view=netcore-3.1

Top comments (0)