This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Update a codebase with nullable reference types to improve null diagnostic warnings

Nullable reference types enable you to declare if variables of a reference type should or shouldn't be assigned a null value. The compiler's static analysis and warnings when your code might dereference null are the most important benefit of this feature. Once enabled, the compiler generates warnings that help you avoid throwing a System.NullReferenceException when your code runs.

If your codebase is relatively small, you can turn on the feature in your project , address warnings, and enjoy the benefits of the improved diagnostics. Larger codebases may require a more structured approach to address warnings over time, enabling the feature for some as you address warnings in different types or files. This article describes different strategies to update a codebase and the tradeoffs associated with these strategies. Before starting your migration, read the conceptual overview of nullable reference types . It covers the compiler's static analysis, null-state values of maybe-null and not-null and the nullable annotations. Once you're familiar with those concepts and terms, you're ready to migrate your code.

Plan your migration

Regardless of how you update your codebase, the goal is that nullable warnings and nullable annotations are enabled in your project. Once you reach that goal, you'll have the <nullable>Enable</nullable> setting in your project. You won't need any of the preprocessor directives to adjust settings elsewhere.

The first choice is setting the default for the project. Your choices are:

Enabling nullable as the default creates more up-front work to add the preprocessor directives to every file. The advantage is that every new code file added to the project will be nullable enabled. Any new work will be nullable aware; only existing code must be updated. Disabling nullable as the default works better if the library is stable, and the main focus of the development is to adopt nullable reference types. You turn on nullable reference types as you annotate APIs. When you've finished, you enable nullable reference types for the entire project. When you create a new file, you must add the preprocessor directives and make it nullable aware. If any developers on your team forget, that new code is now in the backlog of work to make all code nullable aware.

Which of these strategies you pick depends on how much active development is taking place in your project. The more mature and stable your project, the better the second strategy. The more features being developed, the better the first strategy.

The global nullable context does not apply for generated code files. Under either strategy, the nullable context is disabled for any source file marked as generated. This means any APIs in generated files are not annotated. There are four ways a file is marked as generated:

Generators can opt-in using the #nullable preprocessor directive.

Understand contexts and warnings

Enabling warnings and annotations control how the compiler views reference types and nullability. Every type has one of three nullabilities:

The compiler generates warnings based on that nullability:

Each variable has a default nullable state that depends on its nullability:

Before you enable nullable reference types, all declarations in your codebase are nullable oblivious . That's important because it means all reference types have a default null-state of not-null .

Address warnings

If your project uses Entity Framework Core, you should read their guidance on Working with nullable reference types .

When you start your migration, you should start by enabling warnings only. All declarations remain nullable oblivious , but you'll see warnings when you dereference a value after its null-state changes to maybe-null . As you address these warnings, you'll be checking against null in more locations, and your codebase becomes more resilient. To learn specific techniques for different situations, see the article on Techniques to resolve nullable warnings .

You can address warnings and enable annotations in each file or class before continuing with other code. However, it's often more efficient to address the warnings generated while the context is warnings before enabling the type annotations. That way, all types are oblivious until you've addressed the first set of warnings.

Enable type annotations

After addressing the first set of warnings, you can enable the annotation context . This changes reference types from oblivious to nonnullable . All variables declared with var are nullable . This change often introduces new warnings. The first step in addressing the compiler warnings is to use ? annotations on parameter and return types to indicate when arguments or return values may be null . As you do this task, your goal isn't just to fix warnings. The more important goal is to make the compiler understand your intent for potential null values.

Attributes extend type annotations

Several attributes have been added to express additional information about the null state of variables. The rules for your APIs are likely more complicated than not-null or maybe-null for all parameters and return values. Many of your APIs have more complex rules for when variables can or can't be null . In these cases, you'll use attributes to express those rules. The attributes that describe the semantics of your API are found in the article on Attributes that affect nullable analysis .

Once you've addressed all warnings after enabling annotations, you can set the default context for your project to enabled . If you added any pragmas in your code for the nullable annotation or warning context, you can remove them. Over time, you may see new warnings. You may write code that introduces warnings. A library dependency may be updated for nullable reference types. Those updates will change the types in that library from nullable oblivious to either nonnullable or nullable .

You can also explore these concepts in our Learn module on Nullable safety in C# .

Submit and view feedback for

Additional resources

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

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

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.

What does this actually mean?

ΩmegaMan's user avatar

3 Answers 3

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

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:

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

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

...like so:

The corresponding full messages are:

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 .

Community's user avatar

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.

Sean Werkema's user avatar

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.

can be changed to be just

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

James Anderbard's user avatar

Your Answer

Sign up or log in, post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service , privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged c# c#-8.0 nullable-reference-types or ask your own question .

Hot Network Questions

a '#nullable' annotations context

Your privacy

By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy .

Internals of C# nullable reference types - Migrating to nullable reference types - Part 2

Maarten Balliauw

Loves web and HTTP, C#, Kotlin, Azure and application performance. Developer Advocate at JetBrains . Frequent speaker at and organizer of various community events. Likes brewing his own beer.

Internals of C# nullable reference types - Migrating to nullable reference types - Part 2

In the previous post , we saw that with nullable reference types enabled, you get better static flow analysis when working on your code. While nullable reference types don’t give you runtime safety, the design-time and compile-time help is priceless!

In this post, we’ll look at some internals of how C# nullable reference types work, and how the C# compiler and IDE use the nullable annotation context.

In this series:

Under the hood - Intermediate Language (IL)

We’ve already seen there is a difference between nullability for value types and reference types.

Value types are wrapped in a Nullable<T> that gives you a .HasValue property to determine if you can use the wrapped value, or should consider it as null /no value.

Reference types are always nullable, and with nullable reference types (NRT) enabled, you can provide extra context to the IDE and the compiler. With NRT enabled, the C# compiler treats reference types as non-nullable by default, and you can add syntax to annotate them as being nullable.

Consider the following piece of code. Two methods, one returning a nullable integer, the other returning a nullable string.

These two methods are compiled into the following Intermediate Language (IL):

Let’s break this down.

The difference between value types and reference types is very clear in IL: the method that returns a value type, returns a Nullable<int> . The method that returns a reference type returns, well, a reference type. There’s no trace of null vs. non-null!

Or is there… In the IL code, GetString() defines a .custom attribute of type NullableContextAttribute , passing the value (01 00 02 00 00) to the attribute constructor. This is an 8-bit integer value (which exists in IL), and it translates to 2 .

NullableContextAttribute and NullableAttribute

The NullableContextAttribute is used to provide code that consumes this method with some extra metadata, that can then be used by flow analysis.

The C# compiler can add this attribute on types and methods, and supports 3 values:

In our previous example, the value of 2 tells the flow analysis that the method return is annotated, in other words, it has a ? annotation.

Let’s rewrite our GetString() method to be non-nullable:

The NullableContextAttribute will now be created with a value of 1 :

In other words: we have a nullable context enabled, and by default everything in scope is considered to not be annotated (in other words, non-nullable).

One more? What about two! Here are two new methods to explore. In C#:

In IL (just the signatures):

For GetStringA() , the compiler did not emit NullableContextAttribute . Instead, it annotated the method parameters: param [1] and param [3] are annotated with NullableAttribute and a value of 2 . In other words, both parameters should be treated as being annotated (with a ? ). By convention, the presence of a NullableAttribute without NullableContextAttribute also causes everything else to be treated as not annotated (in other words, non-nullable).

For GetStringB() , the compiler emitted two attributes. By default, a NullableContextAttribute with value 2 is applied. In other words, every reference type in this method is annotated with a ? . Except for one: param [2] ( string b ) got a NullableAttribute that has value 1 (no annotation, so non-nullable).

That’s some proper compiler magic going on right there! The C# compiler tries to emit as few attributes as possible, so that flow analysis in the IDE or in a consuming assembly does not have to process too much metadata: just a default nullable context, and any exceptions to that default.

Nullable annotation context

So far, we’ve seen how the nullable context on a type or method changes the Intermediate Language (IL) that is emitted. This nullable context gives consuming code an idea of how to treat nullable reference types.

The next step is telling flow analysis and the compiler what you want to do with that information, by setting the nullable annotation context. The nullable annotation context has 4 settings, and can be defined project-wide, or for every file separately:

Defining nullable annotation context project-wide

To define the nullable annotation context project-wide, you can use the <Nullable>...</Nullable> property in your project file (or any MSBuild file that is included). Here’s an example of a project that sets the nullable annotation context to enable for the entire project:

Defining nullable annotation context per file

You can also define the nullable annotation context in individual files, using the #nullable preprocessor directive:

It’s also possible to use the #nullable directive multiple times per file, and to enable/disable a specific context at various places in the same file. This may be useful when migrating large files to using nullable reference types, as you can split bits that you have already migrated from those you haven’t yet.

Which nullable annotation context should you use?

Good question! As always, “it depends” .

For new projects, it’s a good idea to fully enable the nullable annotation context at the project level. This way, you get the benefits of better static flow analysis from the first line of code you start writing.

For existing projects and code bases, you’ll have to start with deciding what the default setting for your project will be. The end goal of migrating to nullable reference types, is to be able to have nullable warnings and annotations enabled in all projects. But that may not be the best default to start your migration with. Let’s look at some options.

disable as the default

One way to start migration, is by setting disable as the default (or not adding <Nullable /> to your project file at all). Doing so gives you the opportunity to go through your project and add #nullable enable file by file, without drowning in warnings. When your entire project uses nullable reference types, you can switch to enable .

enable as the default

Another way to start migration, is to go all-in and set enable as the project default - this is the end goal so why not set it from the start? This option may (and most probably will) give you lots of warnings to work through. You’ll have to either add nullable annotations, add the null-suppressing operator to some statements, or add #nullable disable for some files as you work your way through the code base.

warnings as the default

With warnings as the project default, null reference analysis is enabled and warnings are shown when your code might dereference null . You’ll see warnings where the compiler’s flow analysis infers potential null references, and you can account for this. Either by adding null checks, or by adding #nullable enable to a file (or a section of a file) and applying ? .

The warnings context is a good way to start exploring an existing codebase and improve null checks. It will make your project safer in terms of nullability, but unless you change the nullable annotation context, you’ll get no benefit of additional annotations in your code base.

Tip: The warnings context helps improve existing code bases with better flow analysis. To make sure you and your fellow developers address these warnings, you can treat these nullable warnings as errors. Set the following two properties in your project file: <Nullable> warnings </Nullable> <WarningsAsErrors> Nullable </WarningsAsErrors>

annotations as the default

When you set annotations as the default, you can start using ? in your project. Flow analysis will be disabled, though, so you won’t get any warnings about potential nullability issues.

In my opinion, this mode does not make a lot of sense. Yes, you can use the language features, but you get no assistance from the IDE or compiler when something is not right.

In this post, we’ve seen some internals of C# nullable reference types. The compiler generates NullableContextAttribute and NullableAttribute usages when compiling C# code to Intermediate Language (IL), providing metadata about nullability.

The metadata added by the compiler is used in various ways, depending on the nullable annotation context you specify in your project or separate files.

In the next post , we’ll look beyond ? and cover the many options for annotating your code and helping out flow analysis to give you better and more reliable results.

Leave a Comment

avatar

0 responses

You may also enjoy, getting rid of warnings with nullable reference types and json object models in c#.

In my blog series, Nullable reference types in C# - Migrating to nullable reference types, we discussed the benefits of enabling nullable reference types in your C# code, and annotating your code so the compiler and IDE can give you more reliable hints about whether a particular variable or property may need to be c... Read more »

Mastodon on your own domain without hosting a server

Like many in the past week, I have been having a serious look at Mastodon as an alternative to Twitter. Read more »

Rate limiting in web applications - Concepts and approaches

Your web application is running fine, and your users are behaving as expected. Life is good! Read more »

ASP.NET Core rate limiting middleware in .NET 7

Rate limiting is a way to control the amount of traffic that a web application or API receives, by limiting the number of requests that can be made in a given period of time. This can help to improve the performance of the site or application, and to prevent it from becoming unresponsive. Read more »

a '#nullable' annotations context

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

@BillWagner

arivoir commented Dec 10, 2018

@dotnet-bot

BillWagner commented Dec 10, 2018

Sorry, something went wrong.

arivoir commented Dec 11, 2018

BillWagner commented Dec 11, 2018

@jcouv

jcouv commented Dec 11, 2018

jcouv commented Dec 11, 2018 • edited

Jcouv commented dec 12, 2018.

@jcouv

jcouv commented Dec 13, 2018

Billwagner commented dec 13, 2018.

@xuhongxu96

xuhongxu96 commented Jan 24, 2019

Jcouv commented jan 24, 2019.

BillWagner commented Jan 24, 2019

@isxaker

isxaker commented Feb 24, 2019

BillWagner commented Mar 4, 2019

Isxaker commented mar 4, 2019.

@bugproof

bugproof commented Mar 15, 2019 • edited

Isxaker commented mar 15, 2019.

@egil

egil commented May 22, 2019 • edited

@AlexanderTaeschner

AlexanderTaeschner commented May 22, 2019

egil commented May 22, 2019

@flcdrg

flcdrg commented May 23, 2019

@dominikjeske

dominikjeske commented Jun 16, 2019

@bdbc78

bdbc78 commented Dec 17, 2019

@Tiramonium

Tiramonium commented Dec 17, 2019 • edited

Bdbc78 commented dec 18, 2019.

@casperOne

KishorTiwari commented Jun 6, 2020

@ata18

ata18 commented May 13, 2021

@PRMerger14

jcouv commented May 17, 2021

Tiramonium commented may 24, 2021, ata18 commented may 24, 2021 • edited, tiramonium commented jun 29, 2021 • edited.

ata18 commented Jun 29, 2021

@FreakyAli

FreakyAli commented Sep 20, 2022

No branches or pull requests

@egil

The .NET Tools Blog

Essential productivity kit for .NET developers

Nullable Reference Types: Contexts and Attributes – A Look at New Language Features in C# 8

Matthias Koch

In this post, we will take another look at Nullable Reference Types. We will learn about the different nullable contexts , and compare the attributes that are being used in Roslyn and in the JetBrains annotations package.

The JetBrains annotations package has been around for roughly 10 years, and it enables users to employ attributes to extend their codebases with additional information about nullability . For instance, the attributes NotNull or CanBeNull could be applied to methods, properties, and parameters. With the introduction of Nullable Reference Types (NRTs) , the C# type system has been extended to make this information a first-class citizen . However, even with NRTs we’re sometimes unable to express the nullability knowledge that we have, which is why NRTs also come with their own set of Roslyn nullable attributes ( System.Diagnostics.CodeAnalysis ).

Let’s start with a discussion of underlying nullable contexts.

Choosing a Nullable Contexts

In order to maximize the benefit we can get from NRTs and nullability analysis in our specific circumstances, it is important to make an educated choice about the nullable contexts to use. There are two different primitive context types:

Note that to compile JetBrains annotations into the output assembly, we have to add JETBRAINS_ANNOTATIONS to the DefineConstants project property.

As a ReSharper or Rider user, enabling just the nullable annotation context could be a good choice for us. This ensures that our codebase profits from the maturity level of ReSharper’s existing external annotations , while the BCL and other libraries are still being annotated with Roslyn attributes. Roslyn annotations are gradually being incorporated into ReSharper’s and Rider’s own analysis. Furthermore, ReSharper’s analysis includes dedicated support for closures and LINQ, allowing you, for instance, to transfer the information from Where(x => x != null) to the next call.

To enable a context for the whole project, we can set the Nullable property (for example, <Nullable>annotations</Nullable> ). We can also use preprocessor directives to enable contexts for a whole file (for example, #nullable warnings ) or limit them to only specific lines using enabling and restoring preprocessor directives (e.g., #nullable enable/restore annotations ). In a simpler form, we can use <Nullable>enable</Nullable> to enable both contexts, or #nullable disable to disable them. Even combinations are allowed so that we can globally enable NRTs on a project, but disable them for specific files and lines, or vice-versa.

Check out this comprehensive example on SharpLab.

Comparing Nullable Attributes

One key difference between ReSharper’s and Roslyn’s annotations is how they are used for input and output values. ReSharper uses the same attributes for both, while Roslyn employs separate attributes:

// Roslyn: MaybeNull, NotNull, AllowNull, DisallowNull [return: MaybeNull] string M([AllowNull] string a) {} [return: NotNull] string M([DisallowNull] string a) {} // ReSharper: CanBeNull, NotNull [CanBeNull] string M([CanBeNull] string a) {} [NotNull] string M([NotNull] string a) {}

While JetBrains annotations might seem a bit simpler to use in this case, the new Roslyn annotations give us the option of creating a more fine-grained contract when needed. For instance, we could mark an attribute to allow null input values but to always return a non- null value:

[AllowNull] // input value! string Name { set => _name = value ?? "N/A"; get => _name; }

Another good example are ref parameters, which could be passed with a null value but then initialized with a real value (other than null ) from the called method:

void Init([AllowNull] ref string a) => s ??= GetValue(); void M(bool weNeedStr) { string? str = null; if (weNeedStr) { Init(ref s); // no warning since null input is allowed Console.WriteLine(s.Length); // no warning since str is not nullable } }

However, if our contract doesn’t require such a fine-grained distinction or if our method is not generic ( T? won’t work, since T must be known to be of either value or nullable reference type), we will likely use the NRT format:

string? NullableString { get; set; } string NonNullableString { get; set; }

For more advanced usages, JetBrains annotations include the ContractAnnotation attribute, which allows us to define pre- and postconditions for a method call using a function definition table syntax . Roslyn, on the other hand, uses a set of simple attributes to express this. Let’s look at some examples. An Assert method can be used to halt the execution if a certain condition is not met:

// Roslyn void Assert([DoesNotReturnIf(false)] bool condition, string message) {} // ReSharper [ContractAnnotation("condition: false => halt")] void Assert(bool condition, string message) {}

Similarly, we can also define an assertion method AssertNotNull to check for null :

// Roslyn void AssertNotNull([NotNull] object? obj, string message) {} // ReSharper [ContractAnnotation("obj: null => halt")] void AssertNotNull(object obj, string message) {}

Another good example is when we access a dictionary using TryGetValue . The returned boolean value indicates whether we’ve found an actual value or not:

// Roslyn bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) {} // ReSharper [ContractAnnotation("=> false; value:null")] bool TryGetValue(TKey key, out TValue value) {}

Last but not least, there’s this simple example of a Fail method that halts the execution no matter what. This knowledge comes in handy during analysis to identify unused code:

// Roslyn [DoesNotReturn] void Fail(string message) {} // ReSharper [ContractAnnotation("=> halt")] void Fail(string message) {}

Strictly speaking, the ContractAnnotation attribute can cover more cases than Roslyn attributes. For instance, we can provide information not only about nullability but also about boolean return types (e.g., obj: null => false ). Moreover the attribute allows us to consider multiple input values at the same time (e.g., format: notnull, obj1: null => halt ). On the other hand, Roslyn attributes are likely to be more accessible initially and less "stringly typed" in some cases.

One final interesting difference is that JetBrains annotations are inherited, while Roslyn attributes aren’t . So with Roslyn, we need to duplicate them on every derived type:

interface ICache<T> { [NotNull] T GetObject(string key); } // Roslyn class SimpleCache<T> : ICache<T> { [NotNull] T GetObject(string key); }

We hope that you enjoyed this series and that ReSharper and Rider can help you make better use of the new language features. Let us know what you think. Any feedback is welcome! Download ReSharper 2020.1 or check out Rider 2020.1 .

Subscribe to Blog updates

By submitting this form, I agree that JetBrains s.r.o. ("JetBrains") may use my name, email address, and location data to send me newsletters, including commercial communications, and to process my personal data for this purpose. I agree that JetBrains may process said data using third-party services for this purpose in accordance with the JetBrains Privacy Policy . I understand that I can revoke this consent at any time in my profile . In addition, an unsubscribe link is included in each email.

Thanks, we've got you!

Discover more

a '#nullable' annotations context

Static Interface Members, Generic Attributes, Auto-Default Structs – Using C# 11 in Rider and ReSharper

The .NET 7 SDK arrived a few months ago, with many .NET developers looking forward to this release and the brand-new C# language features that come along with it. If you haven't put your fingers on it yet, all you need is: Download the latest .NET SDKUpdate your global.json if you have oneUpdate the TargetFramework in your project to net7.0 (only for some features)Update the LangVersion property in your project to 11.0 or preview In this series, we will dive into the most interesting features that are coming with C# 11 and show how we updated ReSharper and Rider to support you in applyin

Matthias Koch

Required Keyword, Checked Operators, nameof Operator Scope – Using C# 11 in Rider and ReSharper

a '#nullable' annotations context

Raw Strings, UTF-8 Strings, and Multiline Interpolations – Using C# 11 in Rider and ReSharper

a '#nullable' annotations context

List and Span Pattern Matching – Using C# 11 in Rider and ReSharper

a '#nullable' annotations context

Nullable Reference Types

Nullable Context Overview

Using Nullable Context in Code

Working With Nullable Reference Types In C# 8.0

Working With Nullable Reference Types In C# 8.0

Working With Nullable Reference Types In C# 8.0

©2023 C# Corner.

We are a boutique consultancy with deep expertise in Azure, Data & Analytics, Azure Synapse Analytics, Power BI, & high performance .NET Development. Based in the UK with a global customer base.

We specialize in modernising data & analytics platforms, and .NET Applications. We help small teams achieve big things.

Who We Help

Whether a global brand, or an ambitious scale-up, we help the small teams who power them, to achieve more.

What We Think

We love to share our hard won learnings, through blogs, talks or thought leadership. This is the good stuff!

If you would like to ask us a question, talk about your requirements, or arrange a chat, we would love to hear from you. Want to know more about how endjin could help you?

C# 8.0 nullable references: embrace the expressiveness

Ian Griffiths

In my previous post in this series on C# 8.0 nullable references, "non-nullable is the new default" I talked about some technical details of the change to the language.

In this post I'm going to talk about one of the benefits this new language feature has to offer.

As well as the obvious win—the ability to detect null-related bugs in your code at compile time—enabling nullable references offers a more subtle benefit: it can improve the expressiveness of your code. This will help anyone reading your code to understand it better.

And for types that represent entities in your application domain, this expressivity may enable you to represent domain model constraints slightly better. Successful code often ends up being read many, many times after being written, so communicative code is the gift that keeps on giving.

When you compile C# with nullable references enabled, the code always states clearly for any variable, parameter, field, or property whether null is an acceptable value.

For each relevant declaration you need to decide whether, say, string is accurate—this is a value that should not be null—or whether there is something about either the logic of the code or perhaps the application domain that means string? is appropriate.

When writing new code, this shouldn't entail significant extra effort: you should know whether you expect null to be a possibility, because you can't write correct code without knowing that. So this information has always been in your head when writing new code, but now you get to record it in the source.

This means that whenever someone next looks at the code they won't need to try to work out what was in your head when you wrote it. (This can be hard enough even if you're looking at your own code from a few months ago.) You have recorded clearly what you had in mind in a way that is visible to anyone reading the code, as well as being visible to the compiler.

Nullable contexts

Strictly speaking, you don't have to enable nullability fully to enjoy this benefit. It is sufficient for the code to be in an enabled nullable annotation context . What's one of those, you might ask?

The nullable reference feature has been designed so that we can enable it gradually.

You don't have to turn it on for the entire project if you don't want to—you can use the #nullable directive to control it for a single file, or even line by line by using the directive multiple times in a file.

You can control two aspects of nullable support independently: you can turn on the warnings without having to state whether your own declared variables, properties, etc. are nullable. You can either add #nullable enable warnings to a source file, or put <Nullable>warnings</Nullable> in your .csproj .

In this case, all references in your own code will be treated as null- oblivious , just like how the compiler treats references defined in external libraries built without nullable annotations. Conversely, you can enable just the ability to express nullability without having to see the resulting warnings with #nullable enable annotations or, in the project file, <Nullable>annotations</Nullable> .

The terminology Microsoft uses is a little odd, but consistent and well defined. Any line of C# code is in two contexts: a nullable warning context and a nullable annotation context. And each of these contexts can be either enabled or disabled.

So if you just switch the feature on completely for a whole project, all your code is in an enabled nullable warning context, and an enabled nullable annotation context.

If you decide you just want the warnings but you don't want to be required to state whether your fields, variables, etc. are nullable, then you can choose to use an enabled nullable warning context, and a disabled nullable annotation context. The setting in the project file determines the default enabled/disabled state for both contexts, and then the #nullable directive can override the setting for either context.

Start expressing yourself

The practical upshot is that even if you're not yet ready to deal with the warnings that show up when you fully enable nullable references, you can still take advantage of the ability to express yourself more precisely today.

When writing new code, you already know whether you mean for particular things to be nullable.

You could write new code in an enabled nullable annotation context, so that you can express that knowledge in the code, even if the rest of your project is not using this new feature at all.

Also worth reading:

C# 8.0 nullable references: inferred (non-)nullness

C# 8.0 nullable references: inferred (non-)nullness

Ian griffiths 28/04/2020.

C# 8.0 nullable references: getting started in an existing codebase

C# 8.0 nullable references: getting started in an existing codebase

Ian griffiths 06/05/2020.

C# 8.0 nullable references: supporting older runtimes

C# 8.0 nullable references: supporting older runtimes

Ian griffiths 20/07/2020, ian griffiths, technical fellow i.

Ian Griffiths

Ian has worked in various aspects of computing, including computer networking, embedded real-time systems, broadcast television systems, medical imaging, and all forms of cloud computing. Ian is a Technical Fellow at endjin, and Microsoft MVP in Developer Technologies. He is the author of O'Reilly's Programming C# 10.0 , and has written Pluralsight courses on WPF ( and here ) and the TPL . He's a maintainer of Reactive Extensions for .NET , Reaqtor , and endjin's 50+ open source projects . Technology brings him joy.

a '#nullable' annotations context

Nullable Types

The last fundamental C# concept that we'll learn about in this course section is nullable types , or using null values. We're already familiar with the concept of null from JavaScript — null means nothing, no value at all. But in a strongly-typed language like C#, how do we set a type to null? In this lesson, we're going to learn exactly how to do that.

When we make want a C# type to possibly be null , we're working with nullable types in C#. Exactly what we need to do to use null depends on whether we're working with a value type or a reference type. Let's first look at nullable values types to learn the basics of using null .

Nullable Value Types

To create a nullable value type, we need to use ? . In formal terms, a nullable value type is expressed as T? , where T is the value type and ? is the syntax used to turn the value type into a nullable type. Let's see an example suing the dotnet-script REPL:

Pretty simple, right? Just add a question mark ? to transform a value type into a nullable value type. Now our test variable can hold an integer or null .

Under the hood, when we use int? , or the T? syntax, our value type is being transformed into the Nullable<T> type that lives in the System namespace. The Nullable<T> type has the helpful HasValue property that can give us information about whether or not the variable is currently null . Here's an example:

Nullable Reference Types

Next, let's look at nullable reference types. With nullable reference types , the syntax and concept is the same, but with a few differences.

The first notable difference is that we need to create a nullable aware context . For example, we'll get an error when we do the following in the dotnet-script REPL:

We can resolve this error by creating a nullable context with #nullable enable :

We create a nullable aware context so that the compiler can track the null state of all reference types within the nullable aware context, and issue warnings and errors when necessary. The compiler will issue a warning if a reference type does not match it's expected state:

If those states are ever not true, then the compiler will let us know. Take the following class for example:

We've created a nullable aware context using the #nullable enable and #nullable disable annotations. However, if we run dotnet build to compile our code, the compiler will issue a warning:

The compiler is letting us know that the NonNullString property should be in a "not-null" state, but is not! The solution is to either make NonNullString nullable, or to give it a value, either in the constructor or a default value. Here's one way we can fix the warning:

To read more about other warnings that the compiler can generate in a nullable aware context, visit the MS Docs on Nullable References and Static Analysis .

The second notable difference between value types and reference types, is that reference types are not turned into Nullable<T> objects, so they don't have access to the HasValue property. Here's an example:

The error lets us know that we're still dealing with the string type, and not a new Nullable<T> . In summary, only nullable value types are turned into Nullable<T> objects.

Other Ways to Create a Nullable Aware Context

We can configure a nullable aware context in a few different ways. As we just saw, we can use the #nullable enable and #nullable disable annotations to sandwich any amount of code to create a nullable aware context:

We can also simply list the #nullable enable annotation on its own at the top of a file to make the entire file have a nullable aware context. That would look something like this:

We can also enable a nullable aware context through our project file ( .csproj ) by adding the <Nullable>enable</Nullable> configuration like so:

When you are just starting out with nullable types, we recommend creating a nullable aware context as needed, instead of setting a project-wide configuration. In the end, it's totally up to you and your project as to what makes the most sense.

Finally, you will not be required to use nullable types on the upcoming independent project. However, we encourage you to experiment with nullable types in the projects you create.

Lesson 16 of 26 Last updated March 17, 2023

IMAGES

  1. [Bug Fix] Add `#nullable enable` To Source Generators by brminnick · Pull Request #300

    a '#nullable' annotations context

  2. CS8632

    a '#nullable' annotations context

  3. Backed Resource Dictionary with TwoWay x:Bind, nullables and WarningsAsErrors generates invalid

    a '#nullable' annotations context

  4. The annotation for nullable reference types should only be used in code within a '#nullable

    a '#nullable' annotations context

  5. Null Value And Null Reference Handling

    a '#nullable' annotations context

  6. C# CS8632 the annotation for nullable reference types should only be used in code within a #

    a '#nullable' annotations context

VIDEO

  1. 👍 C#: The annotation for nullabel referencde types #nullable. represents text as a sequence UTF-16

  2. A Noob Makes an MMO from Scratch in Rust

  3. A Noob Makes an MMO from Scratch in Rust

  4. GCSE POETRY: Power + conflict: Remains

  5. nullables in c#

  6. Option/Maybe type in C#: A 30-second take on monads #shorts

COMMENTS

  1. Nullable reference types

    The nullable annotation context and nullable warning context can be set for a project using the <Nullable> element in your .csproj file. This

  2. Update your codebase to use nullable reference types

    nullable: An annotated reference type, C? , is nullable, but a warning may be issued when the annotation context is disabled. Variables declared

  3. The annotation for nullable reference types should only be used in

    The nullable annotation context and nullable warning context can be set for a project using the Nullable element in your .csproj file. This

  4. Annotating your C# code

    Length as a warning. It uses the information available from the nullable context, which says both in- and output can be null . Luckily, there

  5. Internals of C# nullable reference types

    ... how the C# compiler and IDE use the nullable annotation context. ... be used in code within a '#nullable' annotations context. string?

  6. The annotation for nullable reference types should only be ...

    Warning CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' context.

  7. Nullable Reference Types: Contexts and Attributes

    Nullable annotation context – When this context is enabled, all the nullability information that includes attributes and NRTs in public

  8. Nullable Reference Types

    When Nullable element value is set as enable, a nullable annotation context is enabled and nullable warning context is enabled. As a result, reference type

  9. C# 8.0 nullable references: embrace the expressiveness

    are nullable, then you can choose to use an enabled nullable warning context, and a disabled nullable annotation context. The setting in the

  10. Nullable Types

    We've created a nullable aware context using the #nullable enable and #nullable disable annotations. However, if we run dotnet build to