/flags/csharp/dotnet-build-nullable-enable
C# Nullable=enable (dotnet build -p:Nullable): nullable reference type warnings
- tool
- C#
- command
- dotnet build
- flag
- -p:Nullable=enable|warnings|annotations|disable (<Nullable> in the .csproj)
- default
- disable in the compiler; enable in the .NET 6+ project templates
<Nullable>enable</Nullable> in a .csproj, or dotnet build -p:Nullable=enable for one build, turns on nullable reference types: string means "never null", string? means "may be null", and the compiler warns when the code can dereference or return a null where it should not (CS8602, CS8618, CS8600 and others). The compiler default is disable, but every project created with dotnet new on .NET 6 or later already contains <Nullable>enable</Nullable>.
Same code, two settings
dotnet new console -n App -o . >/dev/null 2>&1
grep -E '<Nullable>' App.csproj
cat > Program.cs <<'EOF'
string? name = args.Length > 0 ? args[0] : null;
Console.WriteLine(name.Length);
class Person { public string Name { get; set; } }
EOF
build() { dotnet build --no-incremental "$@" 2>&1 | grep -oE "(warning|error) CS[0-9]+[^[]*" | sort -u; echo "exit: ${PIPESTATUS[0]}"; }
echo "== Nullable=enable (template default)"
build
echo "== -p:Nullable=disable"
build -p:Nullable=disable
echo "== -p:Nullable=warnings"
build -p:Nullable=warnings
echo "== -p:Nullable=annotations"
build -p:Nullable=annotationsOutput
<Nullable>enable</Nullable> == Nullable=enable (template default) warning CS8602: Dereference of a possibly null reference. warning CS8618: Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. exit: 0 == -p:Nullable=disable warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. exit: 0 == -p:Nullable=warnings warning CS8602: Dereference of a possibly null reference. warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. exit: 0 == -p:Nullable=annotations exit: 0
With enable the compiler reports the possible null dereference (CS8602) and the non-nullable property that is never set (CS8618). With disable those warnings disappear, and the string? annotation itself becomes warning CS8632 because annotations are not allowed in a disabled context. annotations accepts the syntax but produces no null-safety warnings. All are warnings: the build succeeds unless you treat warnings as errors.
--no-incremental matters when you only change a property on the command line: an incremental build may decide that nothing changed and skip the compiler, so the new setting seems to have no effect.
The four values
| Value | Annotations (string?) | Warnings | Typical use |
|---|---|---|---|
| enable | allowed, meaningful | yes | New code; default of the .NET templates |
| warnings | not allowed (CS8632) | yes, for dereferences of values known to be null | First migration step for old code |
| annotations | allowed | no | Libraries that publish annotations before fixing every warning |
| disable | not allowed (CS8632) | no | Compiler default; legacy projects |
Where to set it
<PropertyGroup>
<Nullable>enable</Nullable>
<!-- make the most important nullable warnings fatal -->
<WarningsAsErrors>$(WarningsAsErrors);CS8602;CS8618</WarningsAsErrors>
</PropertyGroup>#nullable enable // from here on in this file
#nullable disable // switch off again
#nullable restore // back to the project settingPutting the property in Directory.Build.props applies it to every project in the repository. The command-line form dotnet build -p:Nullable=enable is handy for a quick check of how many warnings a legacy project would get.
Migration tips
- Start with
warnings, fix what it reports, then switch toenableand add?where null is really allowed. - Fix CS8618 with
requiredmembers, constructor parameters, or initial values, not with= null!, which only silences the warning. - Use
ArgumentNullException.ThrowIfNull(arg)at public API boundaries; the annotation does not stop callers with nullable disabled from passing null. - Generated code (
*.g.cs,*.designer.cs) is treated as disabled unless the file opts in with#nullable enable.
FAQ
Does Nullable=enable change how my program runs?
No. Nullable reference types are a compile-time analysis: the compiler emits warnings and metadata, but the generated code does not check for null at run time. A null can still arrive and cause a NullReferenceException; the warnings show where that can happen.
How do I enable nullable checks for one file only?
Put #nullable enable at the top of the file (and #nullable restore or disable where you want to stop). This is the usual way to migrate a large project file by file before switching the project setting.
Why do I get CS8632 about the annotation for nullable reference types?
The code uses string? or another nullable reference annotation while the nullable context is disabled. Enable it with <Nullable>enable</Nullable> or #nullable enable, or remove the question mark.
Sources
- learn.microsoft.comlearn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/language#nullable
- learn.microsoft.comlearn.microsoft.com/en-us/dotnet/csharp/nullable-references
- learn.microsoft.comlearn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/nullable-warnings