Nullable Reference Types

Course 2 · Ch 6
Nullable Reference Types
Opt-in and warning-only — genuinely less than Kotlin's real, enforced null safety, genuinely more than Java's nothing at all

This chapter is deliberately honest rather than flattering to C#: nullable reference types are a real, useful safety net, but they don't put C# on equal footing with Kotlin's own approach — and being clear about exactly where the line sits matters more than a tidy "C# has null safety too" claim.

The Problem — Null Reference Exceptions

Every reference type in C#, per java1-2's own reference-type material, can normally hold null — dereferencing a null reference throws NullReferenceException at runtime, the same class of bug Java's own NullPointerException represents.

Nullable Reference Types — Opt-In, Not Opt-Out

// with nullable reference types enabled: string name = null; // compiler WARNING — string means "not null" now string? nickname = null; // fine — the ? explicitly allows null

C# 8 (2019) introduced nullable reference types as an opt-in feature — enabled via #nullable enable or project-wide in the .csproj. Once enabled, the compiler treats every reference type as non-nullable by default, requiring an explicit ? (string?) to allow null — inverting the language's own 20-year-old default.

Kotlin's Built-In Null Safety, By Contrast

kotlin1-3's own null safety was never retrofitted — String vs. String? has been the only way Kotlin has worked since Kotlin 1.0. There's no context to enable or disable; it's simply how the type system has always functioned. C#'s version is a compiler feature layered on top of two decades of a language that was nullable-everywhere before C# 8 ever existed.

Java's Total Lack of Either

java1-2/java1-4's own Java has no built-in null-safety mechanism at all — every reference type is always nullable, with zero compiler warning for a potential null dereference, and no opt-in feature to change that. The honest three-way picture: Java offers no protection whatsoever; C# offers an opt-in warning system; Kotlin offers real, type-system-enforced protection from day one.

Nullable Reference Types Are Warnings, Not Guarantees

string? maybeNull = GetName(); Console.WriteLine(maybeNull.Length); // WARNING — but this still compiles and can still throw!

The crucial honesty point: unlike Kotlin's real compile-time errors for unsafe null access, C#'s nullable reference type violations are, by default, only warnings. The code above still compiles cleanly and can still throw NullReferenceException at runtime if the warning is ignored, or if the static analysis is fooled — a value arriving from an un-annotated external library is a common real case. This is a genuine, real limitation, not an edge case to gloss over.

The Null-Forgiving Operator !

string? maybeNull = GetName(); Console.WriteLine(maybeNull!.Length); // "trust me" — suppresses the warning, ZERO runtime check added

! tells the compiler "trust me, this isn't null here," suppressing the warning with no verification of any kind. It's a real escape hatch, and genuinely dangerous if the assertion turns out to be wrong — pure compiler-trust, backed by nothing at runtime.

AspectJavaC#Kotlin (kotlin1-3)
Null-safety mechanismnone at allnullable reference types (opt-in)built into the type system
When introducedneverC# 8 (2019), 20 years inKotlin 1.0, from the start
Violation severityno warning, no errorwarning only, by defaultcompile-time error
Escape hatchn/a! null-forgiving operator!! not-null assertion (also unsafe)
Enable nullable reference types on every new C# project
Retrofitting nullable reference types onto an existing, large codebase is real work — but turning it on for a brand-new project costs nothing and starts every file with the safer, non-null-by-default posture from day one.
A nullable warning is not a compile error — the build still succeeds
Code with unaddressed nullable-reference warnings compiles and runs exactly as before, and can still throw NullReferenceException — genuinely different from Kotlin's real enforcement, where the equivalent code simply wouldn't compile at all. Don't treat "no warnings" as "provably null-safe" the way Kotlin's own guarantee actually is.

Coding Challenges

Challenge 1

With nullable reference types enabled, write a method that assigns null to a plain string variable and show the resulting compiler warning, then fix it by changing the variable's type to string?.

📄 View solution
Challenge 2

Write a method that dereferences a string? that could genuinely be null, deliberately ignoring the compiler warning, and show that the program still compiles and then throws NullReferenceException at runtime when actually run with a null value.

📄 View solution
Challenge 3

Write a short paragraph, as a comment, explaining why Kotlin's null-safety violation is a compile-time error while the equivalent C# nullable-reference-type violation is only a warning, referencing kotlin1-3 directly.

📄 View solution

Chapter 6 Quick Reference

  • Nullable reference types (C# 8, 2019) are opt-in — enabled via #nullable enable or the .csproj, inverting the language's own long-standing nullable-by-default
  • Kotlin's null safety (kotlin1-3) was never retrofitted — String vs. String? has been the only option since Kotlin 1.0
  • Java has no null-safety mechanism at all — no warning, no error, ever
  • C# nullable violations are warnings by default, not errors — the code still compiles and can still throw at runtime, genuinely different from Kotlin's real compile-time enforcement
  • The ! null-forgiving operator suppresses a warning with zero runtime verification — a real, dangerous escape hatch if misused
  • Next chapter: extension methods — the reveal that LINQ's .Where().Select() chaining is powered by them, a direct parallel to java2-3's own lambda reveal