1. Null-coalescing operator (??)
Your knowledge of nullable types won't be complete without knowing how to write concise and safe constructs. In C#, there are two super useful operators for this: null-coalescing (??) and null-conditional (?.).
The ?? operator lets you set a "default value" for a variable that might be null.
Imagine you have a user name that could be null. And if it really is null, you want to show "Guest". Example:
string userName = null;
string displayName = userName != null ? userName : "Guest";
The ?? operator lets you write this more compactly:
string userName = null;
string displayName = userName ?? "Guest";
How it works:
- If the expression on the left isn't null, it's returned as the result.
- If the left side is null, the value on the right is used.
More examples:
int? age = null;
int displayAge = age ?? -1; // -1 — default value
Console.WriteLine(displayAge); // Will print -1
string input = null;
string name = input ?? "Guest";
Console.WriteLine($"Hello, {name}!"); // Hello, Guest!
Operator chaining
You can chain ?? operators:
string result = str1 ?? str2 ?? "Default";
If str1 isn't null, it's used. Otherwise — str2, and if that's also null — then the string "Default".
2. Null-conditional operator (?.)
Another common situation — you want to call a method or access a property of an object, but only if the object itself isn't null. Example:
User user = null;
string displayName = user != null ? user.Name : null;
The ?. operator lets you write this easier:
User user = null;
string displayName = user?.Name;
If the user object is null, the expression won't throw an error, it'll just return null.
Examples:
User user = null;
// Without ?. — you'll get an error
// Console.WriteLine(user.Name); // NullReferenceException
Console.WriteLine(user?.Name); // Safe — prints an empty string or nothing
Console.WriteLine(user?.GetProfileInfo()); // Same thing
User[] users = null;
int? count = users?.Length; // null if users == null
Operator chaining
You can build whole "chains":
string domain = company?.Director?.Email?.Split('@')?[1];
If any part of the path is null, the expression returns null instead of throwing an exception.
Combining with ??
The ?. and ?? operators work great together:
string display = user?.Name ?? "Unknown";
If user or user.Name are null, "Unknown" will be shown.
3. What is ! and why do you need it
Null warning suppression operator
In modern versions of C# (with NRT enabled), the compiler kindly warns you if you're accessing a variable that might be null. But sometimes you know for sure that everything's under control. That's when you use the suppression operator !.
string? possibleNull = GetUserNameMaybeNull();
Console.WriteLine(possibleNull.Length); // Warning: might be null
Console.WriteLine(possibleNull!.Length); // Compiler is silent, but if it's null — you'll get an exception
Important: ! doesn't protect you from errors — it just tells the compiler "trust me". If the variable really is null, you'll get a NullReferenceException.
Where you shouldn't use it
Don't use ! blindly. Good style is to minimize its use. It's better to refactor your code so that null is either impossible or explicitly handled.
4. default — how to get the "default" value of a type
Sometimes you just want to "reset" a variable to its initial state, especially if you don't want to manually write 0, false, or null.
That's what the default keyword is for.
int a = default; // a == 0
bool flag = default; // flag == false
string s = default; // s == null
double? d = default; // d == null
In your mini-app, you can, for example, reset the name or age:
userName = default; // null
userAge = default; // null (if userAge is int?)
5. Differences and nuances: ?, !, default
Even experienced devs sometimes get confused when they see ?, !, and default together. Let's break down who's who.
? — you're allowing null
- For value types: int? x — now x can be null.
- For reference types: string? s — you're explicitly saying the variable can be null (in NRT mode).
! — you're asserting there won't be null
- user!.Name — you're promising the compiler that user is definitely not null.
- Works only in Nullable Reference Types mode.
default — you're asking for the "default" value
- int x = default; — x becomes 0
- string s = default; — s becomes null
Comparison:
| Syntax | What is it? | Where does it work? | What's up with null? |
|---|---|---|---|
|
Nullable value type | Everywhere | You can assign null |
|
Nullable reference type (NRT) | In NRT mode | You can assign null |
|
Null-forgiving operator | In NRT mode | Suppresses warning |
|
Default value | Everywhere | For reference types — it's null |
6. Common mistakes when using ?, ! and default
Mistake #1: thinking ! "heals" null.
Actually, ! just makes the compiler stop complaining. If the value turns out to be null, your program will crash with a NullReferenceException.
Mistake #2: forgetting to check .HasValue before using .Value.
This especially applies to int?, bool? and other nullable types. Without that check, you can get an InvalidOperationException.
Mistake #3: using default where you need a "special" zero meaning.
For example, 0 or false might be valid values, not signals of "emptiness". This can lead to logic bugs.
Mistake #4: not using ? for reference types in NRT mode — or overusing it.
Some folks forget about ?, and the compiler spits out a bunch of warnings. Others slap ? everywhere, even where null never happens. Both hurt readability and code reliability.
GO TO FULL VERSION