The null coalescing operator ?? was introduced in C# 2.0, but the null coalescing assignment operator ??= arrived in C# 8.0. Together, they streamline nullable handling: ?? returns the left operand if it’s not null, otherwise the right operand; ??= assigns the right operand only if the left is null.
Why it matters
?? and ??= eliminate verbose conditional checks. Instead of if (value == null) { value = defaultValue; }, you write value ??= defaultValue. This is especially valuable in configuration loading, API responses, and data initialization patterns where null-or-fallback logic is common.
Practical examples
Use ?? to provide inline fallback values:
string displayName = user.Nickname ?? user.Email ?? "Guest";
Use ??= to lazily initialize fields or cache results:
cache ??= new Dictionary<string, object>();
Combine with LINQ for safe data extraction:
var firstEmail = emails?.FirstOrDefault() ?? "no-reply@example.com";
Cautions
Both operators check strictly for null, not for “falsy” values like empty strings or zero. string.Empty ?? "default" returns string.Empty, not "default". If you need to distinguish null from empty or zero, use explicit checks or nullable-aware patterns.
Null coalescing operator (??)
Provide a default value when the left operand is null.
Valid since C# 8.0
// Before: Verbose null checks
public class UserProfileOld
{
public string? GetDisplayName(User? user)
{
if (user == null)
return "Unknown";
return user.Name;
}
}
// After: Concise with null coalescing
public class UserProfileNew
{
public string GetDisplayName(User? user)
{
return user?.Name ?? "Unknown";
}
}
public class User
{
public string Name { get; set; }
}
Null coalescing assignment (??=)
Assign a value only if the variable is currently null.
Valid since C# 8.0
public class ConfigurationLoader
{
private Dictionary<string, string>? _cache;
// Before: Explicit null assignment
public Dictionary<string, string> GetCacheOld()
{
if (_cache == null)
{
_cache = new Dictionary<string, string>();
}
return _cache;
}
// After: Concise with ??= operator
public Dictionary<string, string> GetCacheNew()
{
_cache ??= new Dictionary<string, string>();
return _cache;
}
// Common pattern: lazy initialization in properties
private List<string>? _errors;
public List<string> Errors
{
get => _errors ??= new List<string>();
}
}
Chaining multiple null checks
Use the null coalescing operator to check multiple fallback values in sequence.
Valid since C# 8.0
public class DataExtractor
{
public class UserPreferences
{
public string? Theme { get; set; }
public string? Locale { get; set; }
public string? Timezone { get; set; }
}
// Chain multiple null checks with ?? operator
public string GetTheme(UserPreferences? prefs, string systemDefault, string hardcodedDefault)
{
// Try user preferences → system default → hardcoded fallback
return prefs?.Theme ?? systemDefault ?? hardcodedDefault;
}
// Real-world: API response handling
public class ApiResponse
{
public string? Message { get; set; }
public string? ErrorCode { get; set; }
}
public string GetUserMessage(ApiResponse? response, string requestId)
{
// If response is null, or message is null, use error code or request ID
return response?.Message ?? response?.ErrorCode ?? $"Request {requestId} failed";
}
// Combining with null-conditional operator
public int? GetConfigValue(Dictionary<string, int?>? configs, string key, int? defaultValue)
{
return configs?[key] ?? defaultValue ?? 0;
}
}