Annotations: ?, !, MaybeNull & NotNull
Use ? for nullable types, ! for null-forgiving, and attributes like MaybeNull and NotNullWhen for precise flow analysis.
Beyond ? and !: Nullability Attributes
The basic ? annotation and null check handle most cases, but some patterns require more expressive annotations. The System.Diagnostics.CodeAnalysis namespace provides attributes that give the compiler deeper insight into null flow.
MaybeNull and NotNull
[MaybeNull] tells the compiler a non-nullable return type may actually be null in practice (e.g., a generic method). [NotNull] promises a nullable parameter will not be null after the call.
using System.Diagnostics.CodeAnalysis;
// [MaybeNull]: return might be null even though T is non-nullable
[return: MaybeNull]
public T Find<T>(int id)
{
// Returns default(T) which is null for reference types
return _cache.TryGetValue(id, out var val) ? val : default!;
}
// [NotNull]: after this call, output is guaranteed non-null
public static void EnsureNotNull<T>(
[NotNull] ref T? value,
T defaultValue) where T : class
{
value ??= defaultValue;
}All lessons in this course
- Enabling & Understanding NRT
- Annotations: ?, !, MaybeNull & NotNull
- Null-Conditional & Null-Coalescing Operators
- Migrating a Codebase to NRT