1. Intro
Let's imagine this situation. You're storing data about cats in your code. And besides, say, the cat's age, you also want to store states like "no data", "age unknown". You could store 0, but zero is a totally valid age.
int age = 0; // Cat's age. But what does 0 mean? Is it a kitten or "unknown"?
The problem is, int only takes integer values, and "no data" isn't one of them. If age was a string, you could assign null. But with numbers, that trick doesn't work:
int age = null; // Error! You can't assign null to a variable of type int
That's because value types (structs like int, double, DateTime, bool) always store something. They don't have an "empty" state (unlike reference types, where null just means no object).
Real-life example:
Trying to get around this, devs sometimes invent "special" values: like -1 or int.MaxValue for "no value". But that's ugly, inconvenient, and risky: it's easy to mix up a real value with a placeholder.
2. Nullable Type: a type that can be "null"
The Idea
What if we let regular numbers (and all value types, really) store not just a value, but also null?
In C#, this is done with a special wrapper class Nullable<T>. This class can hold either a value of type T, or nothing (meaning null).
Syntax
int? age = null; // The age variable can be a number or null
When the C# compiler sees this, it actually treats it as:
Nullable<int> age = new Nullable<int>(); // The age variable doesn't have a value
Or, if you want to set a value explicitly:
Nullable<int> age = new Nullable<int>(42); // age holds the number 42
So, the question mark turns any value type into its nullable version:
- int? (same as Nullable<int>)
- double?
- DateTime?
- and any other struct
Nullable wrappers for the most popular struct types
| Regular Type | Nullable Type | Example |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
You can also write it the long way, using the wrapper class:
Nullable<int> pages = null;
But writing int? is shorter, and honestly, cooler.
3. How to use a nullable type
Assigning and checking
Assigning null works just like with reference types:
Declaration and assignment
int? temperature = null;
temperature = 25;
Checking for null
How do you know if your nullable type actually has a value?
if (temperature != null)
{
Console.WriteLine($"Temperature: {temperature}");
}
else
{
Console.WriteLine("No temperature data");
}
Nullable type properties: .HasValue and .Value
Nullable types have two important properties:
- .HasValue — returns true if the variable has something in it (not null).
- .Value — the value itself, if it exists (otherwise you'll get an exception).
int? temperature = null;
if (temperature.HasValue) //no exception here!
{
Console.WriteLine($"Temperature: {temperature.Value}°C");
}
else
{
Console.WriteLine("Temperature unknown");
}
This code will work: temperature isn't null, it holds null inside. And you can always call the HasValue property on a Nullable type. But trying to get .Value when it's not there? Bad idea, you'll get an InvalidOperationException. Always check for a value first!
Shorter output syntax
A lot of times, you can just use the nullable variable — and C# will figure it out:
int? hours = null;
Console.WriteLine(hours); // prints nothing (just blank)
hours = 10;
Console.WriteLine(hours); // prints 10
4. Operations and nullable types: math, comparisons, conversions
Operations: what happens?
If one of the operands is nullable, the result is also nullable.
And if one of the operands is null, the result is also null.
int? a = 5;
int? b = null;
int? sum = a + b; // sum == null
int? c = 10;
int? d = 15;
int? total = c + d; // total == 25
Comparison operations
You can compare nullable types with regular numbers:
int? score = null;
if (score > 0) Console.WriteLine("Awesome result!");
else Console.WriteLine("No result"); // This branch will run
If score is null, the condition score > 0 will be false.
Explicit and implicit conversion
- From regular type to nullable: automatic.
- From nullable to regular: only if it's not null (otherwise, exception).
int? x = 3;
int y = x.Value; // Ok, if x isn't null
// Implicit
int? z = y;
5. Nullable and methods: parameters and return values
Returning nullable values
If a method can't always return a result, use nullable types as the return value:
// The method looks for a user by login and returns their age if found, otherwise null
int? FindUserAge(string login)
{
// ... search logic here
return null; // if not found
}
Nullable parameters
You can accept nullable values as parameters, to make it clear: "might be there, might not".
void PrintTemperature(int? temp)
{
if (temp == null)
Console.WriteLine("Temperature unknown");
else
Console.WriteLine($"Temperature: {temp} degrees");
}
GO TO FULL VERSION