CodeGym /Courses /C# SELF /Intro to Nullable Reference Types

Intro to Nullable Reference Types

C# SELF
Level 14, Lesson 1
Available

1. The NullReferenceException Problem

Pretty much every beginner (and not just beginners) C# programmer has run into this scary message at least once:


System.NullReferenceException: Object reference not set to an instance of an object.

Here's a classic example:

string hello = null;
Console.WriteLine(hello.Length); // BOOM! NullReferenceException

The idea is simple: you're trying to access an object that doesn't exist. That is, your variable points to null — an empty spot instead of an object.

Why is this mistake so easy to make?

Because most reference types in C# have historically been able to take the value null. Programmers often forgot to check if a variable actually points to an object, and as a result, got hit with the infamous NullReferenceException.

If programmers got a dollar for every NullReferenceException, they'd have written their own operating system by now.

Why is this a problem?

Earlier, we learned how to make values of type int? or double? equal to null — for cases when we need to explicitly say "there's no value" (like a field in a database that might be empty).

But reference types (like string, any classes, arrays, etc.) have always been able to be null. That's been the case since C# was born. It's convenient, but also dangerous — because the language never forced us to think: "Could this reference be empty?"

2. Evolution: Nullable Reference Types (NRT)

Five years ago, a new feature was introduced that totally flipped the way we deal with NullReferenceException — Nullable Reference Types (NRT).

string s = "hello";   // not-nullable reference
string? maybe = null; // nullable reference
The difference between not-nullable and nullable reference types

Main idea:

  • Strictly separate reference variables that definitely can't be null from those that can be empty.
  • Start helping the programmer see potential null problems right at compile time, not at runtime (when it's already too late).

In newer versions of C#, the approach to declaring reference variables changed: by default, you can't assign null unless you explicitly allow it.

It's that simple — just one ? character, and the meaning of your variable changes instantly!

3. Turning on Nullable Reference Types

By default, this strict syntax is turned off in most projects, so old code doesn't break. But modern project templates in Visual Studio, Rider, and .NET CLI already create projects with NRT enabled — or at least strongly recommend you do it.

To check if NRT is enabled in your project, look for this line in your .csproj file:

<Nullable>enable</Nullable>
Enabling NRT in your project

If you don't see this line, you can add it manually — it's safe.

How does this affect your code?

  • If NRT is off (old behavior): string s = null; — no errors, nobody cares.
  • If it's on: the compiler will complain if you try to shove null into something that shouldn't be null.

4. NRT Examples

Simple example


#nullable enable // This line turns on NRT checks for this file

string notNullable = "Hello";
string notNullable2 = null; // COMPILATION ERROR!
string? nullableString = null; // All good, we explicitly allowed null
Example of strict null checking

In the first line, we declared a string that always has to point to a real object.
In the third — we declared a string that can be null.

Checking for null

void PrintLength(string? s)
{
    // The compiler will complain: "But what if s == null?"
    Console.WriteLine(s.Length); 
    
    // This way - it's fine
    if (s != null)
    {
        Console.WriteLine(s.Length); 
    }
}

The compiler now helps you remember to check!

Compiler warnings

If you ignore the warning and still try to access a nullable variable without checking — you'll get a new (and super useful!) compiler warning. It's not an error (your program will still build), but the yellow "lightbulb" is a hint: "You sure about this, buddy?"

Comparing modes:

C# Type Can be null? Legacy mode (before NRT) NRT mode (#nullable enable)
int No No No
int? Yes Yes Yes
string Yes No (always allowed) No (not allowed by default)
string? Yes No Yes

5. Tips: How and Why to Use NRT

  • Fewer bugs: Less random crashes because of null, more happiness for devs and users.
  • Clearer code: It's obvious right away what can be empty and what always has to be filled in.
  • Compiler help: Time to admit it — it's really trying to help us! NRT warnings are a goldmine for catching potential bugs.

Where this is a lifesaver

  • In big projects where lots of people work on the same codebase.
  • In APIs and public libraries — to make it clear to others what they can and can't do.
  • Anywhere reliability matters (like banking apps, medical systems, etc.)

6. Typical mistakes and gotchas

  • "Forgot the ?"
    You assign null to a regular string (string s = null;) — and the compiler complains, because by default now a regular string can't be null.
  • "Went overboard with ?"
    You make every variable string? just so the compiler doesn't yell at you. The whole point is to carefully mark where empty values are actually allowed.
  • "Misunderstood the warning"
    You ignore the warning, then get hit with a NullReferenceException where you thought the compiler had your back.
2
Task
C# SELF, level 14, lesson 1
Locked
Declaring Nullable Reference Types
Declaring Nullable Reference Types
2
Task
C# SELF, level 14, lesson 1
Locked
Null Check
Null Check
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION