C# 14 introduces null-conditional assignment (?.=), a small but highly practical language enhancement that reduces repetitive null checks when updating objects. It builds on the existing null-conditional operator (?.) and allows assignments to occur only when the target object is not null.
This feature improves code readability, reduces boilerplate, and helps prevent NullReferenceException in real-world applications where optional objects are common.
What Is Null-Conditional Assignment (?.=)?
Assigns a value only if the target object is not null
Skips the assignment silently when the target is null
Prevents
NullReferenceExceptionWorks with fields and properties
Designed for defensive and optional updates
Syntax
target?.Property = value;
Before C# 14: How Null Checks Were Handled
Manual Null Check
if (user != null)
{
user.IsActive = true;
}
Verbose and repetitive
Easy to forget in large codebases
Increases nesting and visual noise
Null-Conditional Operator (Read-Only)
user?.Activate();
Works for method calls and reads
Does not support assignment
Still requires explicit checks for updates
C# 14 Null-Conditional Assignment in Action
user?.IsActive = true;
Assignment occurs only if
useris not nullNo exception is thrown
Code intent is immediately clear
Real-Life Scenarios
1.Optional Logging Service
logger?.LastMessage = "Order processed successfully";
Logging may be disabled in some environments
Business logic remains unaffected
Eliminates unnecessary branching
2. UI or ViewModel Updates
viewModel?.StatusMessage = "Loading completed";
UI elements may not yet be initialized
Common in MVVM, desktop, and mobile apps
Prevents runtime crashes
3. Cache Metadata Updates
cacheEntry?.LastAccessed = DateTime.UtcNow;
Cache entries may expire or be unavailable
Keeps caching logic concise
Improves application stability
Complex Scenarios
1. Order Processing with Optional Audit Trail
public void CompleteOrder(Order order, AuditTrail audit)
{
order.Status = OrderStatus.Completed;
order.CompletedAt = DateTime.UtcNow;
audit?.LastAction = "Order completed";
audit?.UpdatedAt = DateTime.UtcNow;
}
Order completion is mandatory
Audit tracking is optional
Keeps core business logic clean
2. Middleware with Optional Telemetry
public async Task InvokeAsync(HttpContext context, Telemetry telemetry)
{
var start = DateTime.UtcNow;
await _next(context);
telemetry?.RequestPath = context.Request.Path;
telemetry?.DurationMs =
(DateTime.UtcNow - start).TotalMilliseconds;
}
Telemetry may be disabled for performance
Avoids cluttering middleware code
Maintains clear execution flow
3. Async Service with Optional Cache Sync
public async Task<UserProfile> GetProfileAsync(
int userId,
IProfileCache cache)
{
var profile = await LoadFromDatabaseAsync(userId);
cache?.Profile = profile;
cache?.LastRefresh = DateTime.UtcNow;
return profile;
}
Database remains the source of truth
Cache is an optional optimization
Clean separation of concerns
4. Plugin-Based Application
public void Initialize(App app, IPluginContext pluginContext)
{
app.Start();
pluginContext?.State = PluginState.Initialized;
pluginContext?.InitializedAt = DateTime.UtcNow;
}
Plugins may or may not be installed
Core app logic is unaffected
Safer extensibility model
Join the conversation! Your thoughts help the community grow.