Skip to Content
Course content

211: Nullable Value Types (Nullable<T>) In Depth

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I remember a junior dev I worked with a few years ago who spent an entire afternoon chasing a ghost in a reporting tool. He was pulling a "Last Login Day" value from a SQL database. Some users had never logged in, so the database column was NULL. In C#, he mapped this field to a standard int. Because a value type like int cannot be null, the framework assigned it the default value of 0. Suddenly, users who had never logged in were being reported as having logged in on January 1, 1900. It looked like massive data corruption, but it was actually just a fundamental misunderstanding of how value types behave when they encounter a void.

In C#, we have a strict divide between reference types (which can be null) and value types (which cannot). An int, a bool, or a DateTime always holds a value. This is great for performance and memory predictability, but it's a nightmare when you're dealing with optional data—like a middle name that might be missing or a sensor reading that failed to report.

The Gap Between Value Types and Databases

To solve this, C# gives us Nullable<T>. You've likely seen the shorthand version, like int? or bool?. It's important to realize that int? is just syntactic sugar for Nullable<int>. When you use this, you aren't actually changing the int into a reference type; instead, you're wrapping that int inside a special structure that can track whether a value is actually present.


int? optionalAge = null; 
bool? isSubscriptionActive = null;
DateTime? lastLogin = null;

if (optionalAge == null) 
{
    Console.WriteLine("Age was not provided.");
}

I've seen people try to treat these like regular integers immediately, which leads to compiler errors. You can't pass an int? into a method that expects a regular int without explicitly handling the null case first. The compiler is protecting you from the exact bug my colleague encountered years ago.

Peeking Under the Hood of Nullable<T>

Since Nullable<T> is a struct, it has two primary properties you need to know: HasValue and Value. The HasValue property is a boolean that tells you if the variable is actually holding data. The Value property gives you the underlying value of type T.

Here is the danger zone: if you try to access .Value when HasValue is false, C# will throw an InvalidOperationException. It doesn't return a default; it crashes. I always tell my students to treat .Value like a loaded gun—don't pull the trigger unless you've checked the safety first.


int? score = null;

// This will crash the program!
// int finalScore = score.Value; 

if (score.HasValue) 
{
    int finalScore = score.Value;
    Console.WriteLine($"Your score is: {finalScore}");
}

Handling the Absence of Value Safely

Checking HasValue everywhere makes your code look cluttered and "stuttery." In a professional codebase, we rarely do that. Instead, we use the null-coalescing operator (??). This operator allows you to provide a fallback value in a single, readable line. If the left side is null, it takes the right side.


int? sensorReading = null;

// If sensorReading is null, use -1 as a sentinel value
int cleanReading = sensorReading ?? -1; 

Console.WriteLine($"Reading: {cleanReading}"); // Output: Reading: -1

You can also use the null-conditional operator (?.) if you're dealing with nullable types that have methods or properties, though this is more common with reference types. For value types, the combination of ?? and GetValueOrDefault() are your primary tools for keeping your logic clean and your application stable.




📋 Practical Task

Exercise: Repairing the Weather Station Data Processor

You have been handed a piece of code for a weather station that is incorrectly reporting temperatures. The system currently uses a standard double for temperature, meaning missing readings (which should be null) are being treated as 0.0 degrees. This is skewing the average temperature calculations during winter months.

Your Task:

  1. Modify the WeatherReading class to use a nullable double? for the Temperature property.
  2. Update the CalculateAverage method so that it ignores readings where the temperature is null, rather than counting them as 0.0.
  3. Use the null-coalescing operator (??) to print "N/A" or the temperature value when displaying the logs.

using System;
using System.Collections.Generic;
using System.Linq;

public class WeatherReading
{
    public DateTime Timestamp { get; set; }
    public double Temperature { get; set; } // TODO: Make this nullable
}

public class WeatherProcessor
{
    public static void Main()
    {
        var readings = new List<WeatherReading>
        {
            new WeatherReading { Timestamp = DateTime.Now, Temperature = 22.5 },
            new WeatherReading { Timestamp = DateTime.Now.AddHours(-1), Temperature = 0.0 }, // Is this 0 degrees or missing?
            new WeatherReading { Timestamp = DateTime.Now.AddHours(-2), Temperature = 19.8 }
        };

        // TODO: Fix the average calculation to ignore nulls
        double average = readings.Average(r => r.Temperature);
        Console.WriteLine($"Average Temp: {average}");
    }
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.