Skip to Content
Course content

228: Enabling Nullable Context

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

I can't tell you how many times I've had to debug a production crash that looked exactly like this. You're looking at a piece of code that seems perfectly logical, but it's hiding a landmine that only explodes when a specific user enters a specific set of data.

public class UserProfile
{
    public string FirstName { get; set; }
    public string MiddleName { get; set; } 
    public string LastName { get; set; }
}

public class ProfilePrinter
{
    public void PrintFullName(UserProfile profile)
    {
        // This looks fine, right?
        Console.WriteLine($"User: {profile.FirstName} {profile.MiddleName.ToUpper()} {profile.LastName}");
    }
}

The problem here is the MiddleName. Not everyone has one. If a UserProfile is passed in where MiddleName is null, the call to ToUpper() will throw a NullReferenceException. The worst part? The compiler didn't give you a single warning. It just let you walk right into the wall.

The Invisible Danger of Default Reference Types

By default, in older versions of C# (or in projects where the nullable context is disabled), all reference types are "nullable." This means string can be a string, or it can be null, and the compiler doesn't care which one it is. It assumes you know what you're doing. But as we've seen, we aren't always right.

When the nullable context is disabled, the compiler treats string and string? as exactly the same thing. There is no distinction. You are essentially flying without a radar.

Turning on the Safety Rails

To fix this, we need to enable the Nullable Reference Types feature. This isn't a change to your code logic, but a change to how the compiler views your types. You do this in your project file (the .csproj file).

Open your .csproj and add the <Nullable>enable</Nullable> tag inside the PropertyGroup:

<PropertyGroup>
  <TargetFramework>net8.0</TargetFramework>
  <ImplicitUsings>enable</ImplicitUsings>
  <Nullable>enable</Nullable>
</PropertyGroup>

The moment you save this file, your IDE is going to light up with warnings. Don't panic—this is actually the feature working. The compiler is now telling you, "Hey, you said MiddleName is a string, but you never assigned it a value, so it's going to be null by default. That's a problem."

Fixing the Warnings with Intent

Now that the context is enabled, we have to be explicit about our intent. If a value can be null, we mark it with a ?. If it cannot be null, we leave it as is and ensure it's initialized.

public class UserProfile
{
    // These are required. The compiler will warn if they aren't set.
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;

    // This is optional. The '?' tells the compiler "I know this might be null."
    public string? MiddleName { get; set; }
}

public class ProfilePrinter
{
    public void PrintFullName(UserProfile profile)
    {
        // The compiler now warns us that MiddleName might be null here!
        // We fix it using the null-conditional operator.
        string middle = profile.MiddleName?.ToUpper() ?? ""; 
        Console.WriteLine($"User: {profile.FirstName} {middle} {profile.LastName}");
    }
}

By enabling the nullable context, we've shifted the discovery of the bug from Runtime (where the user sees a crash) to Compile-time (where you see a yellow squiggly line). I highly recommend enabling this on every new project you start; it forces you to think about the "null case" before you even hit the run button.




📋 Practical Task

Hardening the Customer Address Validator

You have been handed a Customer class used in a shipping system. Currently, the project has <Nullable>enable</Nullable> turned on, but the previous developer ignored all the compiler warnings, leaving the code fragile.

Your Task: Fix the following code so that there are zero compiler warnings and no possibility of a NullReferenceException during the GetShippingLabel call.

public class Customer
{
    public string Name { get; set; }
    public Address ShippingAddress { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string? ZipCode { get; set; } // ZipCode is optional
}

public class ShippingService
{
    public string GetShippingLabel(Customer customer)
    {
        // WARNING: customer.ShippingAddress might be null
        // WARNING: customer.ShippingAddress.Street might be null
        return $"Ship to: {customer.Name} at {customer.ShippingAddress.Street}, {customer.ShippingAddress.City}";
    }
}

Requirements:

  • Ensure Name, Street, and City are treated as non-nullable and initialized (use string.Empty or a constructor).
  • Keep ZipCode as optional.
  • Update GetShippingLabel to safely handle the possibility that the ShippingAddress object itself might be null, returning "No Address Provided" if it is.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.