The null coalescing assignment operator (??=) introduced in C# 8.0 provides a cleaner way to assign a value to a variable only when it's null. Let's see how and when to use it effectively.
Quick Example
// Instead of writing this:
if (myVariable == null)
myVariable = defaultValue;
// You can write this:
myVariable ??= defaultValue;
Real-World Examples
Simple Property Initialization
public class UserSettings
{
private List<string> _preferences;
public List<string> Preferences
{
get
{
_preferences ??= new List<string>();
return _preferences;
}
}
}
Service Caching
public class ServiceCache
{
private ApiClient _client;
public ApiClient GetClient()
{
_client ??= new ApiClient("https://api.example.com");
return _client;
}
}
Lazy Configuration Loading
public class ConfigurationManager
{
private Dictionary<string, string> _settings;
public string GetSetting(string key)
{
_settings ??= LoadSettingsFromFile();
return _settings.TryGetValue(key, out var value) ? value : null;
}
private Dictionary<string, string> LoadSettingsFromFile()
{
// Load settings logic here
return new Dictionary<string, string>();
}
}
Common Gotchas
Reference vs Value Types
The operator works differently with value types - they need to be nullable:
// This won't compile
int count ??= 1;
// This works
int? count ??= 1;
Chaining Operations
// You can chain the operator
string result = first ??= second ??= "default";
// Equivalent to:
if (first == null)
{
if (second == null)
{
second = "default";
}
first = second;
}
result = first;
Thread Safety
The operator is not thread-safe by default:
// Not thread-safe
public class SharedCache
{
private static Dictionary<string, object> _cache;
public object GetItem(string key)
{
// Multiple threads could evaluate null simultaneously
_cache ??= new Dictionary<string, object>();
return _cache.GetValueOrDefault(key);
}
}
// Thread-safe version
public class SharedCache
{
private static Dictionary<string, object> _cache;
private static readonly object _lock = new object();
public object GetItem(string key)
{
lock (_lock)
{
_cache ??= new Dictionary<string, object>();
return _cache.GetValueOrDefault(key);
}
}
}
The null coalescing assignment operator is compiled to efficient IL code. It generally performs the same as an explicit null check:
// These compile to similar IL
obj ??= new object();
if (obj == null)
obj = new object();
When to Use It
✅ Good use cases:
- Lazy initialization of properties
- Caching values
- Setting default values for nullable types
- Simplifying null checks in property getters
❌ Avoid using when:
- You need thread-safe initialization (use
Lazy<T>
instead)
- The right-hand expression has side effects
- You need more complex null-checking logic
Visual Studio Tips
You can use Quick Actions (Ctrl+.) to convert between traditional null checks and the ??= operator. Look for the suggestion "Use null coalescing assignment" when you have a pattern like:
if (variable == null)
variable = value;
Version Compatibility
This feature requires:
- C# 8.0 or later
- .NET Core 3.0+ or .NET Standard 2.1+
- Visual Studio 2019+