Understanding Nullable Value Types in C#
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
}
}
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 an 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.
What this does is, essentially, it wraps our value type of int
in a Nullable struct; which allows us to assign it a 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;
}
}
Now we can simply assign null
s 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; }
}
This is beautiful, and behind the scenes it still wraps then 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 null
s 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 anInvalidOperationException
if you try to access a value whose.Value
property isfalse
.GetValueOrDefault()
- Underlying value or default. Eg: for an integer whose value has not been set, will return0
-.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 ✌️