The annotation for nullable reference types should only be used in code within a '#nullable' context
Asked Answered
R

6

269

I have a console app to try out the C# 8 null reference types. Switched the project to build with lang ver C# 8.

Then the following code results in a warning.

class Program
{
    static void Main(string[] args)
    {
        string? message = "Hello World";
        string message2 = null;

        Console.WriteLine(message);
        Console.WriteLine(message2);

        // The annotation for nullable reference types should only be used in code within a '#nullable' context
    }
}

What does this actually mean?

Repertoire answered 3/4, 2019 at 9:53 Comment(0)
R
405

For anyone ending up here. You can put #nullable enable on top of the file for a file-by-file approach as suggested by @Marc in the comments.

You can also use combinations of #nullable enable/disable to annotate just parts of the file

class Program
{
    static void Main(string[] args)
    {
#nullable enable
        string? message = "Hello World";
#nullable disable
        string message2 = null;

        Console.WriteLine(message);
        Console.WriteLine(message2);
    }
}

Here's a link to the docs. https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references#nullable-contexts

Nullable contexts enable fine-grained control for how the compiler interprets reference type variables. The nullable annotation context of any given source line is either enabled or disabled. You can think of the pre-C# 8.0 compiler as compiling all your code in a disabled nullable context: any reference type may be null. The nullable warnings context may also be enabled or disabled. The nullable warnings context specifies the warnings generated by the compiler using its flow analysis.

The nullable annotation context and nullable warning context can be set for a project using the Nullable element in your .csproj file. This element configures how the compiler interprets the nullability of types and what warnings are generated. Valid settings are:

  • enable:
    • The nullable annotation context is enabled. The nullable warning context is enabled.
    • Variables of a reference type, string for example, are non-nullable. All nullability warnings are enabled.
  • warnings:
    • The nullable annotation context is disabled. The nullable warning context is enabled.
    • Variables of a reference type are oblivious. All nullability warnings are enabled.
  • annotations:
    • The nullable annotation context is enabled. The nullable warning context is disabled.
    • Variables of a reference type, string for example, are non-nullable. All nullability warnings are disabled.
  • disable:
    • The nullable annotation context is disabled. The nullable warning context is disabled.
    • Variables of a reference type are oblivious, just like earlier versions of C#. All nullability warnings are disabled.

In your .csproj file, simply add <Nullable>enable</Nullable> in the relevant <PropertyGroup> element (your project file may have separate <PropertyGroup> elements for each project configuration name).

So your project file should look like this one:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

To display the nullable messages as errors instead of warnings, add this to your project file:

<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>

...like so:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <Nullable>enable</Nullable>
    <WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
  </PropertyGroup>

</Project>

The corresponding full messages are:

  • CS8600: Converting null literal or possible null value to non-nullable type.
  • CS8602: Possible dereference of a null reference.
  • CS8603: Possible null reference return.

You can also use directives to set these same contexts anywhere in your project:

  • #nullable enable: Sets the nullable annotation context and nullable warning context to enabled.
  • #nullable disable: Sets the nullable annotation context and nullable warning context to disabled.
  • #nullable restore: Restores the nullable annotation context and nullable warning context to the project settings.
  • #nullable disable warnings: Set the nullable warning context to disabled.
  • #nullable enable warnings: Set the nullable warning context to enabled.
  • #nullable restore warnings: Restores the nullable warning context to the project settings.
  • #nullable disable annotations: Set the nullable annotation context to disabled.
  • #nullable enable annotations: Set the nullable annotation context to enabled.
  • #nullable restore annotations: Restores the annotation warning context to the project settings.

By default, nullable annotation and warning contexts are disabled. That means that your existing code compiles without changes and without generating any new warnings.

Note that pre-release versions of C# 8.0 and Visual Studio 2019 also supported safeonly, however this option has since been removed and is not present in the final shipping C# 8.0. Additionally the pre-release versions used #pragma warning restore nullable but the released version uses #nullable restore warnings.

Repertoire answered 3/4, 2019 at 10:4 Comment(8)
Might also be worth expanding to cover #nullable disable and #nullable restore or a link to documentation covering all of them.Bohlen
Answer edited to reflect the renaming of property NullableContextOptions to Nullable in VS 16.1.Chaulmoogra
Good answer. But for some reason approach with changing project parameters does not work for me. Using Visual Studio Code.Blend
Apparently the safeonly option is no-longer defined: github.com/dotnet/docs/issues/13227Grasp
I got Named attribute argument expected ERROR when using <Nullable>enable</Nullable>Snitch
nice answer. I would add CS8618 to warnings as errors to cover: "Non-nullable property 'PbxUniqueCallId' is uninitialized. Consider declaring the property as nullable."Fortuity
I have this in my csproj so I shouldn't need to put anything extra in the code <Nullable>enable</Nullable> but I still get the errorBibliographer
The nullable directive can also now be defined for all projects in a directory using Directory.Build.props, see blog.johnnyreilly.com/2021/07/14/…Virendra
P
38

I got this same error too, and I spent a few days fighting it, for a reason not described by the other answers: There are special (undocumented?) rules in the Roslyn C# compiler for generated code that uses the nullable feature, and until I upgraded Visual Studio 2019 to the latest version (16.4+), the error message was exactly the same unhelpful message as above — despite having <Nullable>enabled</Nullable> in my project file.

But in the latest versions of Visual Studio 2019, they've upgraded the error message to this:

warning CS8669: The annotation for nullable reference types should only be used in code within a ‘#nullable’ annotations context. Auto-generated code requires an explicit ‘#nullable’ directive in source.

(Emphasis added to the newly-added part of the error message.)

So if you're seeing this error message in any .generated.cs files you're emitting, those files need to have #nullable enable added to them explicitly — Roslyn apparently always ignores the project-level <Nullable>enable</Nullable> setting for generated code.

Peres answered 23/12, 2019 at 19:50 Comment(7)
The documentation for C#'s nullable contexts for generated files is under learn.microsoft.com/en-us/dotnet/csharp/…. They've added an "Important" subsection that lists the 4 ways a file is marked as generated.Cletus
Thanks for the link, and that's a good find! It's good that they've finally added the documentation; but it's bad that it was a completely-undocumented "magic" rule for several years prior.Peres
I also got the CS8632 warning even in a newly created .NET 6.0 project, own code, within an older solution that doesn't use the C#8 nullable reftypes. The new project has <Nullable>enable</Nullable> set. Solution: delete all bin and obj directories in the solution, as also recommended for net6 migration by MS. "Clean Solution" was not sufficient in my case.Phonic
any idea why this is like this?Forthwith
Users are often not in control of the code generators they use. Therefore enabling nullable in their project doesn't necessarily mean the generated code in their project is accurately nullable-annotated. We want the code generator itself to indicate that it has onboarded to nullable. Therefore we default the generated files to nullable-disabled, expecting that the generator will update to include #nullable enable when it onboards to nullable.Teethe
What a complete and utter messShotten
Agree with Richard Hammond, what a mess. Undoubtedly part of this is the price we pay for MS worshipping at the altar of backward compatibility. The link in the first comment says ? is "not allowed" with a reference type if I set Nullable to disabled, but it is both allowed and I get the warning. And this is in non-generated code.Basswood
T
9

All reference types, like string or objects, are nullable by nature. It means that in pre-C#8.0 we can just do like this: string message = null

In post-C#8.0 if option <Nullable>enable</Nullable> or #nullable enable directive are set, reference types are treated like non-nullable by default. It means we have to explicitly indicate that they are nullable, like this: string?. As we did before for int?

That's it (more details in the popular answers).

Option name is a bit misleading because <Nullable>enable</Nullable> is actually disabling default nullability behaviour of reference types, and visa versa. So, think <Nullable> as "explicit nullability"

T answered 22/7, 2023 at 10:23 Comment(0)
S
0

My issue was that I was setting the declaration of an object with a nullable ? operator. That was unneeded in my case and simply removing it solved the issue.

Example

public class Foo() {}

public class Bar() {
    private Foo? foo;
}

can be changed to be just

public class Foo() {}

public class Bar() {
    private Foo foo;
}

Just note any checks for HasValue will need to be also changed to check it like this:

foo != null
Seattle answered 28/9, 2022 at 1:19 Comment(0)
O
0

Update:

You can solve CS8669 like this inside a razor file:

@{#pragma warning disable CS8669}
<fluent-search @ref=Element
               value=@Value
               current-value="@BindConverter.FormatValue(CurrentValue)"
               @onchange="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @oninput="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @attributes="AdditionalAttributes">
    @ChildContent
</fluent-search>
@{#pragma warning restore CS8669}

This will not work:

@{#nullable enable}
<fluent-search @ref=Element
               value=@Value
               current-value="@BindConverter.FormatValue(CurrentValue)"
               @onchange="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @oninput="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @attributes="AdditionalAttributes">
    @ChildContent
</fluent-search>
@{#nullable disable}

Original:

I got the error CS8669:

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.

However I could not make it go away with #nullable enable or #pragma warning disable CS8669 for the following Blazor component:

#nullable enable
@namespace Microsoft.Fast.Components.FluentUI
@inherits FluentSearch

#pragma warning disable CS8669
<fluent-search @ref=Element
               value=@Value
               current-value="@BindConverter.FormatValue(CurrentValue)"
               @onchange="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @oninput="@(EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString))"
               @attributes="AdditionalAttributes">
    @ChildContent
</fluent-search>
#pragma warning restore CS8669

enter image description here

The only thing that worked was using "Exclude specific warnings as errors" in Project properties:

enter image description here

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <TreatWarningsAsErrors>True</TreatWarningsAsErrors>
    <WarningLevel>9999</WarningLevel>
    <WarningsNotAsErrors>0436;NU1608;CS8669</WarningsNotAsErrors>
    <Optimize>False</Optimize>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <TreatWarningsAsErrors>True</TreatWarningsAsErrors>
    <WarningLevel>9999</WarningLevel>
    <WarningsNotAsErrors>0436;NU1608;CS8669</WarningsNotAsErrors>
  </PropertyGroup>
Outfielder answered 16/5, 2023 at 12:29 Comment(0)
T
0

We were getting this error at runtime when loading a cshtml page. It turns out we had runtime compilation on in our site. Disabling that fixed the error for us.

We had to remove this line in our Program.cs file. builder.Services.AddControllersWithViews().AddRazorRuntimeCompilation();

Transcaucasia answered 26/7, 2023 at 17:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.