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
- 6 minutes to read
- 8 contributors
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:
- Nullable disable as the default : disable is the default if you don't add a Nullable element to your project file. Use this default when you're not actively adding new files to the codebase. The main activity is to update the library to use nullable reference types. Using this default means you add a nullable preprocessor directive to each file as you update its code.
- Nullable enable as the default : Set this default when you're actively developing new features. You want all new code to benefit from nullable reference types and nullable static analysis. Using this default means you must add a #nullable disable to the top of each file. You'll remove these preprocessor directives as you address the warnings in each file.
- Nullable warnings as the default : Choose this default for a two-phase migration. In the first phase, address warnings. In the second phase, turn on annotations for declaring a variable's expected null-state . Using this default means you must add a #nullable disable to the top of each file.
- Nullable annotations as the default. Annotate code before addressing warnings.
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:
- In the .editorconfig, specify generated_code = true in a section that applies to that file.
- Put <auto-generated> or <auto-generated/> in a comment at the top of the file. It can be on any line in that comment, but the comment block must be the first element in the file.
- Start the file name with TemporaryGeneratedFile_
- End the file name with .designer.cs , .generated.cs , .g.cs , or .g.i.cs .
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:
- oblivious : All reference types are nullable oblivious when the annotation context is disabled.
- nonnullable : An unannotated reference type, C is nonnullable when the annotation context is enabled.
- nullable : An annotated reference type, C? , is nullable , but a warning may be issued when the annotation context is disabled. Variables declared with var are nullable when the annotation context is enabled.
The compiler generates warnings based on that nullability:
- nonnullable types cause warnings if a potential null value is assigned to them.
- nullable types cause warnings if they dereferenced when maybe-null .
- oblivious types cause warnings if they're dereferenced when maybe-null and the warning context is enabled.
Each variable has a default nullable state that depends on its nullability:
- Nullable variables have a default null-state of maybe-null .
- Non-nullable variables have a default null-state of not-null .
- Nullable oblivious variables have a default null-state of not-null .
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
- Stack Overflow Public questions & answers
- Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers
- Talent Build your employer brand
- Advertising Reach developers & technologists worldwide
- About the company
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?
- nullable-reference-types
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:
- 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 .
- 20 nice answer; I like that it covers both approaches – Marc Gravell Apr 3, 2019 at 10:05
- 1 Might also be worth expanding to cover #nullable disable and #nullable restore or a link to documentation covering all of them. – Damien_The_Unbeliever Apr 3, 2019 at 10:20
- Answer edited to reflect the renaming of property NullableContextOptions to Nullable in VS 16.1. – Drew Noakes May 23, 2019 at 3:30
- 4 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 error – Christian Findlay Aug 24, 2020 at 0:20
- 1 amazingly thorough answer! – kodybrown Feb 10, 2021 at 15:04
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.

- 2 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. – Bill Menees Mar 22, 2021 at 11:53
- 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. – Sean Werkema Mar 22, 2021 at 15:24
- 1 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. – Erik Hart Jan 28, 2022 at 12:15
- 1 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. – Rikki Gibson Jul 20, 2022 at 4:44
- 4 What a complete and utter mess – Richard Hammond Aug 14, 2022 at 20:48
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:
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 .
- The Overflow Blog
- Can Stack Overflow save the day?
- Let’s talk large language models (Ep. 546)
- Featured on Meta
- We've added a "Necessary cookies only" option to the cookie consent popup
- The Stack Exchange reputation system: What's working? What's not?
- Launching the CI/CD and R Collectives and community editing features for...
- Staging Ground Beta 1 Recap, and Reviewers needed for Beta 2
- Temporary policy: ChatGPT is banned
Hot Network Questions
- If a man's name is on the birth certificate, but all were aware that he is not the blood father, and the couple separates, is he responsible legally?
- "Miss" as a form of address to a married teacher in Bethan Roberts' "My Policeman"
- How long to carry sort?
- Was Silicon Valley Bank's failure due to "Trump-era deregulation", and/or do Democrats share blame for it?
- How should I understand bar number notation used by stage management to mark cue points in an opera score?
- Practical usefulness of PCA
- Can someone be prosecuted for something that was legal when they did it?
- How is the ICC warrant supposed to restrict Putin's travel abroad given that he's in possession of diplomatic immunity?
- Error "Illegal pream-token" when using using LaTeX3 / expl3 with package array
- Can a coincidence be evidence of a god?
- Round ends for dashes
- Was Jesus' early church composed primarily of Torah-abiding Jews?
- I booked an Airbnb in NYC, what do I risk?
- Check if PID still running
- What justifies "virtue ethics" or other forms of ethics emphasizing "virtues"?
- Three Latin questions (adjectives vs nouns, meaning of a sentence and dictionary recommendation)
- Did I give the right advice to my father about his 401k being down?
- How can I restore my default .bashrc file again?
- What interpretation do REML/fREML values provide in generalized additive models (GAMs)?
- Why is geothermal heat insignificant to surface temperature?
- What is the name for the spherical type of attitude indicator that also shows heading?
- A ZFC example of a Menger space which is not Scheepers
- Rock Runner feat Pathfinder 2e
- How can I stay longer in my flight stop cities without much additional flight cost?
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 .

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.
- Antwerp, Belgium
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:
- Nullable reference types in C#
- Internals of C# nullable reference types (this post)
- Annotating your C# code
- Techniques and tools to update your project
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.
- int? GetInt() => 1; is compiled into a .method that returns a value type [System.Runtime]System.Nullable`1<int32> . The method body pushes the value 1 onto the stack, and creates a new System.Nullable`1<int32> that takes the first element from the stack (the 1 that was just pushed). Finally, this new object is returned.
- string? GetString() => ""; is compiled into a .method that returns a string . The method body pushes a new object reference to a string literal stored in the assembly metadata (see this post about string literals for more background), and returns it.
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:
- 0 - Oblivious - Use the default, pre-C#8 behaviour (everything is maybe null )
- 1 - Not annotated - Consider the scope as not annotated by default (in other words, every reference type is non-nullable by default)
- 2 - Annotated - Consider the scope as annotated by default (in other words, every reference type has an implicit ? slapped onto it)
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:
- disable - makes the compiler behave like it did pre-C# 8.0 (no NRT). You can not use ? on reference types.
- enable - enables null reference analysis and all language features.
- warnings - enables null reference analysis, and shows warnings when code might dereference null .
- annotations - enables language features and lets you use ? , but does not enable null reference analysis.
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:
- #nullable enable - enable nullable annotation context and nullable warning context
- #nullable disable - disable nullable annotation context and nullable warning context
- #nullable restore - set the nullable annotation context and nullable warning context to the project-level setting
- #nullable enable/disable/restore warnings - enable/disable/restore just the nullable warning context
- #nullable enable/disable/restore annotations - enable/disable/restore just the nullable annotation context
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

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 »
- No suggested jump to results
- Notifications
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
arivoir commented Dec 10, 2018
BillWagner commented Dec 10, 2018
Sorry, something went wrong.
arivoir commented Dec 11, 2018
- 👍 4 reactions
BillWagner commented Dec 11, 2018
jcouv commented Dec 11, 2018
- 👍 5 reactions
- 👍 1 reaction
jcouv commented Dec 11, 2018 • edited
Jcouv commented dec 12, 2018.
jcouv commented Dec 13, 2018
Billwagner commented dec 13, 2018.
xuhongxu96 commented Jan 24, 2019
Jcouv commented jan 24, 2019.
- 👍 7 reactions
- 🎉 1 reaction
BillWagner commented Jan 24, 2019

isxaker commented Feb 24, 2019
- 👍 2 reactions
BillWagner commented Mar 4, 2019
Isxaker commented mar 4, 2019.
bugproof commented Mar 15, 2019 • edited
Isxaker commented mar 15, 2019.
egil commented May 22, 2019 • edited
AlexanderTaeschner commented May 22, 2019
- 👍 9 reactions
egil commented May 22, 2019
flcdrg commented May 23, 2019
dominikjeske commented Jun 16, 2019
bdbc78 commented Dec 17, 2019
Tiramonium commented Dec 17, 2019 • edited
Bdbc78 commented dec 18, 2019.
KishorTiwari commented Jun 6, 2020
ata18 commented May 13, 2021
jcouv commented May 17, 2021
Tiramonium commented may 24, 2021, ata18 commented may 24, 2021 • edited, tiramonium commented jun 29, 2021 • edited.
- 👎 1 reaction
ata18 commented Jun 29, 2021
FreakyAli commented Sep 20, 2022
No branches or pull requests
- Blogs by Topic
The .NET Tools Blog
Essential productivity kit for .NET developers
- Follow .NET Tools:
Nullable Reference Types: Contexts and Attributes – A Look at New Language Features in C# 8
- Indices, Ranges, and Null-coalescing Assignments
- Switch Expressions and Pattern-Based Usings
- Recursive Pattern Matching
- Async Streams
- Nullable Reference Types: Migrating a Codebase
- Nullable Reference Types: Contexts and Attributes
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:
- Nullable warning context – When this context is enabled, Roslyn analysis will run and report warnings related to NRTs and static flow analysis. ReSharper’s analysis won’t consider its own NotNull / CanBeNull attributes to match the exact behavior of the compiler. However, it still uses static flow analysis and checks replicated compiler warnings (as code inspections) for more immediate feedback.
- Nullable annotation context – When this context is enabled, all the nullability information that includes attributes and NRTs in public signatures is exposed in the compiled assembly through the NullableContext and Nullable attributes.
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

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
Required Keyword, Checked Operators, nameof Operator Scope – Using C# 11 in Rider and ReSharper

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

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

- Learn C# 8.0
- Default Interface Implementation
- Switch Expressions And Pattern Matching
- Ranges And Indices
Nullable Reference Types
- Async Streams, Static Local Functions
- Read-only Struct Member
- New using Declarations
- Null-Coalescing Assignment Operator
- Static Local Functions
- 'Nullable Enable' To Avoid Null Reference Exception
Nullable Context Overview
- The nullable annotation context: It can be enabled or disabled for any given source line. You can consider the pre-C# 8 compilers for the disabled annotation context.
- The nullable warnings context: It may be set to enabled, disabled, or safeonly. The nullable warnings context specifies the warnings generated by the compiler using its flow analysis.
- <LangVersion>8.0</LangVersion>
- <Nullable>enable</Nullable>
- #nullable enable: Sets the nullable annotation context and nullable warning context to enabled.
- #nullable disable: Sets the nullable annotation context and nullable warning
- #nullable safeonly: Set the nullable annotation context to enabled, and the warning context to safeonly.
- #pragma warning disable nullable: Set the nullable warning context to disabled.
- #pragma warning enable nullable: Set the nullable warning context to enabled.
- #pragma warning safeonly nullable: Sets the nullable warning context to safeonly.
- The behavior is the same as previous versions of C#.
- The nullable references approach doesn't exist as all reference variables may be assigned to null.
- No warnings are generated when a reference type variable is dereferenced. The null-forgiving operator (i.e. !) may not be used.
- Any reference type variable is a non-nullable reference. Any non-nullable reference may be dereferenced safely.
- Any nullable reference type may be null. If the value is known to be non-null after static analysis, the compiler warns you.
- You can use the null-forgiving operator ! to declare that a nullable reference isn't null.
Using Nullable Context in Code
- public static void ExecuteNonNullableReferenceType()
- {
- #nullable enable
- string name = null ;
- var myName = name.ToString();
- #nullable restore
- }

- public static void ExecuteNullableReferenceType()
- string ? name = null ;

- public static void ExecuteNullableReferenceType2()
- var myName = name!.ToString(); // Null Forgiving Operator

- Crystal Reports Tutorials
- Learn Visual Studio Code
- Learn ASP.NET MVC 5.0
- Build a Unity Game In 1 Hr
- Learn Flutter
- Learn SSRS In 11 Hours
- React.js for Beginners
- Learn Angular 8 In 25 Days
- Learn WCF in 10 Hours
- Learn C# 7.x
- C# Asynchronous Programming
- Angular 8 in 10 Days
- Introduction to MongoDB
- Learn MongoDB in 15 Days
- Learn Machine Learning With Python
- Learn Internet of Things in 21 Days
- Mastering SQL
- Learn Python
- Learn Django in 20 Days
- Learn JavaScript
- Build Progressive Web Apps
- Python in 4 Hours
©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

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
Ian griffiths 28/04/2020.

C# 8.0 nullable references: getting started in an existing codebase
Ian griffiths 06/05/2020.

C# 8.0 nullable references: supporting older runtimes
Ian griffiths 20/07/2020, ian griffiths, technical fellow i.

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.
- C# and .NET
- Test-Driven Development With C#
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:
- Non-null reference types are expected to have a "not-null" state.
- Nullable reference types are expected to have a "maybe-null" 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
VIDEO
COMMENTS
The nullable annotation context and nullable warning context can be set for a project using the <Nullable> element in your .csproj file. This
nullable: An annotated reference type, C? , is nullable, but a warning may be issued when the annotation context is disabled. Variables declared
The nullable annotation context and nullable warning context can be set for a project using the Nullable element in your .csproj file. This
Length as a warning. It uses the information available from the nullable context, which says both in- and output can be null . Luckily, there
... how the C# compiler and IDE use the nullable annotation context. ... be used in code within a '#nullable' annotations context. string?
Warning CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' context.
Nullable annotation context – When this context is enabled, all the nullability information that includes attributes and NRTs in public
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
are nullable, then you can choose to use an enabled nullable warning context, and a disabled nullable annotation context. The setting in the
We've created a nullable aware context using the #nullable enable and #nullable disable annotations. However, if we run dotnet build to