The Traps of Nullable<T> in C#: a Practical Guide with Tiny Examples
Nullable<T> is not a mini reference type and pretending it is will bite you. This guide walks through how nullable value types work at runtime, how boxing really behaves, and what operator lifting does. You will see small, runnable C# snippets that expose common traps and safe patterns. Learn how to handle equality, pattern matching, and generics without surprises.
If I had a nickel for every time I saw int? treated like string?, I could probably fund my coffee habit. Nullable value types look friendly, but under the hood they have their own rules. Treat them like references and they will absolutely prank you in production. Let's take a few minutes to demystify Nullable<T> with a tour of how it is represented at runtime, what happens during boxing, how operator lifting works, and the common traps that bite even experienced C# devs.
The real shape of Nullable
T? is syntax sugar for System.Nullable<T>, which is a struct with two fields in spirit: a bool HasValue and a T Value. It preserves value semantics while adding an explicit notion of absence.
Before we do anything fancy, let us get our footing.
int? hp = null;
Console.WriteLine(hp.HasValue); // False
Console.WriteLine(hp.GetValueOrDefault()); // 0
int? shields = 100;
Console.WriteLine(shields.HasValue); // True
Console.WriteLine(shields.Value); // 100
Reading .Value when HasValue is false throws InvalidOperationException, not NullReferenceException. That difference matters when you are debugging.
int? mana = null;
// Console.WriteLine(mana.Value); // Would throw InvalidOperationException
Console.WriteLine(mana ?? 50); // Use a safe fallback