Null Value And Null Reference Handling - C#6 To C# 9 New Features - Day One

This article is a part of the article series on C# New Features, and this article series will cover all the new features of C#, which have been introduced in the last 4-5 years along with upcoming new features. If we talk about language versions, then this series covers C# 6, C# 7.0, C# 7.1, C# 7.2, and C# 7.3, and C# 8, and upcoming features of C# 9.0.
 
 
In the current article, we are going to discuss null values and null reference handing in C#. Many new features and syntax have been introduced in C# from C# 6 to C# 8 versions to handle and manipulate null values and null references. Some more null values handling features are proposed in C# 9.0, which is the upcoming version of C#. Following are the list of improvements for null handling along with exiting null handling options in C#, which we are going to discuss.
 
Before C# 6.0
  • Nullable value type (‘nullable<value type>’ or ‘value type?’)
  • Null Coalescing Operator (‘??’)
C# 6.0: New null handling operator 
  • Null conditional operator for member access (‘?.’)
  • Null conditional operator for index-based access (‘?[]’)
C# 8.0: Multiple Changes for null handling 
  • Null-coalescing assignment (‘??=’)
  • Non-nullable Reference Type
  • Nullable reference Type (‘reference type?’)
  • Null-forgiving operator (‘!’)
C# 9.0: Planned and upcoming feature for null handling
  • Simplified parameter null validation
  • Target typed null coalescing (`??`) expression
  • Nullable-enhanced common type
  • Ternary operation with nullable int and double 
In the last 4-5 years, many new features have been introduced in C#, and if we talk about the number of features, including major and minor features, then it would be around a hundred, and to explain those hundred features and their real-time uses we need to understand and compare with the already existing features as well. So we will be discussing those features as well as when they are required.
 
Following is a pictorial representation of new & upcoming versions of C#.
representation of new & upcoming versions of C#
 
Note
C# 9.0 is not officially released, and it is still in the discussion and development phase, so we cannot be sure of how many of those features will be part of the C# 9.0 official release. It may get changed. But I would like to discuss C# 9.0 proposed features as well so that we can have a better understanding of where we are moving for C# upcoming and future releases.
 
Let’s start with the brief overview of the existing features of C#, which are available before C# 6.0. That way we can have a better understanding when discussing new features. However, we are not covering the existing features in detail.
 

Nullable value type (‘nullable<value type>’ or ‘value type?’)

 
Nullable value type (‘nullable<value type>’ or ‘value type?’) is not from the beginning of C#. It was introduced with C# 2.0 to allow value types accepting the null value.
 
Syntax
  1. Nullable<T>  variable;  
 Example
  1. Nullable<int> x= null;  
Simplified Syntax
  1. T? variable;  
Example
  1. int? x = null;  
The latest version of Visual Studio also gives us a hint to simplify it.
 
Visual Studio hint
 
Tips: Nullable<T> is a struct, and its fully qualified name is System.Nullable<T>
  1. namespace System  
  2. {  
  3.     //Omitted for brevity  
  4.     [NonVersionableAttribute]  
  5.     public struct Nullable<T> where T : struct  
  6.     {  
  7.           
  8. //Omitted for brevity  
  9.     }  
  10. }  

Null Coalescing Operator (‘??’)

 
If left-hand side expression is not null, then it returns the value of left-hand side expression; otherwise, it evaluates right-hand side expression and returns the evaluated value of right-hand side expression as a result.
 
Null Coalescing Operator (‘??’) is a very powerful operator, but it is underused in C# coding. I have seen in many real-time projects where people are writing long multiline codes for a null check rather than just using the Operator ‘??’.
 
Syntax
 
<left-hand side expression> ?? <right-hand side expression>
 
Example 
  1. private static int Add(int x, int? y)  
  2.         {  
  3.             if (y==null)  
  4.             {  
  5.                 y = 0;  
  6.             }  
  7.             return x + (int)y;  
  8.         }   

 It can be written as
  1. private static int Add(int x, int? y)    
  2.         {    
  3.             return x + y ?? 0;    
  4.         }    

Null conditional operator for member access (?.)

 
Syntax
 
<instanceName>?.<memberName>
 
Null conditional operator for member access
We, as C# developers have seen the below exception message multiple times.
 
System.NullReferenceException
 
'Object reference not set to an instance of an object.' The following is a screenshot of ‘System.NullReferenceException’ in debug mode.
 
‘System.NullReferenceException’ in debug mode
 
Complete code
  1. using System;  
  2. using static System.Console;  
  3.   
  4. namespace BankeCSharpBook  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             Customer customer = null;  
  11.             WriteLine(customer.Id);  
  12.             WriteLine("Execution completed");  
  13.         }  
  14.     }  
  15.     class Customer  
  16.     {  
  17.         public int Id { getset; }  
  18.     }  
  19.   
  20. }  
So, we can see that we are trying to access a property from the instance which has a null reference, and that is why we are getting this exception message.
 
If we search online, we can find many discussions regarding the ‘System.NullReferenceException’ we can find a very significant number of discussions. Following is a screenshot that is taken from https://stackoverflow.com/ for the same.
 
NullReferenceException-Example 2
 

How to minimize the ‘System.NullReferenceException’

 
We can use “Null conditional operator for member access (?.)” to get rid of this exception and breaking the code.
 
Just replace the code
  1. WriteLine(customer.Id);  
 with 
  1. WriteLine(customer?.Id);  
Finally, the error is gone.
 
NullReferenceException-Handled example 2
 
Many times, we know the reason for that, but if it occurs in a production environment, we will be in trouble because, after that, the complete unit of the functionality stops working if error not appropriately handled.
 
<instanceName>?.<memberName> ?? <defaultvalue>
 
Null-conditional Operator with Null-Coalescing operator
 
Just take the same example which we have used earlier have a look at the below code snippets,
  1. using System;  
  2. using static System.Console;  
  3.   
  4. namespace BankeCSharpBook  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             Customer customer = null;  
  11.             WriteLine($"customer name :{customer?.Name}");  
  12.             WriteLine($"Name Length:{customer?.Name?.Length}");  
  13.             WriteLine("Execution completed");  
  14.         }  
  15.     }  
  16.     class Customer  
  17.     {  
  18.         public int Id { getset; }  
  19.         public string Name { getset; }  
  20.     }  
  21.   
  22. }  
Output
 
output Null-conditional Operator with Null-Coalescing operator 
 
We can see that the “null reference exception” has been handled, and it is returning null instead of throwing the null reference exception.
 
However, it would be better if we display some custom values instead of null. So replace the below line of code
  1. WriteLine($"customer name :{customer?.Name}");   
  2. WriteLine($"Name Length:{customer?.Name?.Length}");  
With
  1. WriteLine($"customer name :{customer?.Name?? "not provided or null"}");  
  2. WriteLine($"Name Length:{customer?.Name?.Length?? 0}");  
Notes
  1. In this article, we have used string interpolation and using static features as well during code representation, But we are not covering those features in this article; it will be covered in future articles
  2. The “Null conditional operator” is also known with other names such as “Safe navigator operator”, “Null propagation operator”, “Elvis operator”.

NULL conditional operator for index-based access (?[])

 
So far, we have seen how we can handle the null reference exception and how to use “null- Coalescing” operator along with the “null conditional operator”. However, the question arises that how we can handle the exception when working with Indexers because we cannot use (?.) with Indexers.
 
To work with Indexer, we have another syntax for “Null Conditional operator”.
 
Syntax for Indexers
 
<instanceName>?[<memberName>]
NULL conditional operator for index-based access
Have a look at the below screenshot, and we will notice that we are getting the same exception, i.e. null reference exception with indexes as well.
 
NullReferenceException-Example 3
 
Now replace the line
  1. WriteLine($"Third odd number is : {oddNumbers[2]}");  
 By
  1. WriteLine($"Third odd number is : {oddNumbers?[2]}");  
And re-run the code, and we can see that it is no longer throwing an exception. Because now we are using a Null conditional operator for index-based access (?[])
 
Complete code 
  1. using static System.Console;  
  2. namespace IndexBasedNullConditinal  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             int[] oddNumbers = { 1, 3, 5, 7, 9 };  
  9.             oddNumbers = null//I am assigning null value intentetinally just for demo purposes but in real project it would be coming from somewhere else.  
  10.             WriteLine($"Third odd number is : {oddNumbers?[2]}");  
  11.         }  
  12.     }  
  13.   
  14. }  

Null-coalescing assignment Operator (??=)

Null-coalescing assignment Operator
Null-coalescing assignment (??=) operator has been introduced with C# 8.0. We know that “Null-coalescing operator (??)” is already in C# before C# 8.0 version, but it has some limitations, and it does not support the assignment.
 
Let's have a look at the below code snippet,
  1. List<string> fruits = null;    
  2. (fruits ?? new List<string>()).Add("apple");   
  3. Console.WriteLine(fruits.Count);  
Compile and run the above code snippet. We can see that it is throwing null reference exception because the new instance which we are creating after checking with null-Coalescing is not being assigned. Thus the original variable “fruits” still having null reference and so we cannot add any value to that list reference.
 
NullReferenceException-Example 4
 
So we are again facing the issue of null reference exception and the solution is “null-coalescing assignment (??=)” i.e. replace the code 
  1. (fruits ?? new List<string>()).Add("apple");  
By 
  1. (fruits ??= new List<string>()).Add("apple");  
And re-run the code. We can see that the code is no longer throwing the “NullReferenceException” exception, and we are good to go. Following is the screenshot of the code and its output after using the Null-coalescing assignment “??=” operator.
 
If we try to achieve the same through the earlier version of the code, then we have to write like below code snippet.
  1. List<string> fruits = null;    
  2. if (fruits == null)    
  3. {    
  4. fruits = new List<string>();   
  5. }  
  6.   
  7. fruits.Add("apple");    
  8. Console.WriteLine(fruits.Count);  
 The above code snippet can be easily replaced by the below code snippet in C# 8.
  1. List<string> fruits = null;    
  2. (fruits ??= new List<string>()).Add("apple");  
  3. Console.WriteLine(fruits.Count);  
Complete code
  1. using System;  
  2. using System.Collections.Generic;  
  3. using static System.Console;  
  4.   
  5. namespace BankeCSharpBook  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             List<string> fruits = null;  
  12.             (fruits ??= new List<string>()).Add("apple");  
  13.             WriteLine(fruits.Count);  
  14.         }  
  15.     }  
  16.   
  17.     class Customer  
  18.     {  
  19.         public int? Id { getset; }  
  20.         public string Name { getset; }  
  21.     }  
  22.   
  23. }  
Output
 
1 
 

Nullable Reference Type and Non-Nullable Reference Type

 
So far, we have seen many scenarios where the null references exception may arise. You may be wondering if there is an option so that a reference type should accept a null value or not.
 
Yes, that why in C# 8.0, we can create “nullable reference type” and “non-nullable reference type”.
 
Non-Nullable reference type feature has been introduced with C# 8.0. Enabling non-nullable reference types in C# 8 is a breaking change, and we may need to make changes in our existing code to compile and work as intended. That is why it is not enabled by default, and we need to enable it by ourselves.
 
Let us have a look at the changed syntaxes
 
Non-Nullable Reference Type Example 1
 
Non-Nullable Reference Type Example 2 
Following is the complete code snippet for the same.
  1. namespace NullableExample1  
  2. {  
  3.     class Program  
  4.     {  
  5.         static void Main(string[] args)  
  6.         {  
  7.             //C# 8.0 -- nullable instance of Customer type  
  8.             Customer? cust1 = new Customer();  
  9.   
  10.             //C# 8.0 -- non-nullable instance of Customer type  
  11.             Customer cust2 = new Customer();  
  12.         }  
  13.     }  
  14.   
  15.     public class Customer  
  16.     {  
  17.         //C# 8.0 -- non-nullable string  
  18.         public string CustomerId { getset; }  
  19.   
  20.         //C# 8.0 -- nullable string  
  21.         public string? Name { getset; }  
  22.     }  
  23. }  
If we have not enabled the nullable type feature, then the above code gives compilation warnings, and we get the warning messages like below.
 
The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
 
nullable annotations context error
 

Enabling nullable and non-nullable reference type feature in C# 8.0

 
Let us enable the feature to see if there is any warning after that or not.
 
Step 1
 
Edit the project file,
 
Edit your project file
 
Step 2
 
Go to the <PropertyGroup> section in the “.csproj” file.
 
Step 3
 
Add the below line of code at the location highlighted in the screenshot.
 
<Nullable>enable</Nullable>
 
Nullable enable at project level
 
Step 4
 
Save the project file and go back to the application.
 
Now we can see that the warning messages get disappeared from the Visual Studio Error List window.
 
But we may get other warning messages if we are not using non-nullable reference types correctly or if it is an error-prone code. Following is a screenshot from Visual Studio, which gives another warning after enabling the '#nullable' context.
 
Nullable enable warning-1
Enabling the '#nullable' context and using the nullable and non-nullable reference types does not guarantee us that we cannot get a null reference exception, but its probability can be minimized.
 

Do we need to worry about this breaking change?

 
No, we do not need to worry about this breaking change if we are migrating our existing C# application to use the latest version feature but do not want to use the '#nullable' context. Then it is okay we can use your existing C# code with C# 8.0 version, and it works correctly in the same manner as working earlier and does not give any warning.
 
That’s why the '#nullable' context is disabled by default in C# 8.0. This means we can enjoy other feature of C# 8.0 without opting for '#nullable' context.
 

How is it beneficial?

 
To understand how it is beneficial to us, have a look at the below code snippet.
  1. class Customer {  
  2.     public string CustomerId {  
  3.         get;  
  4.         set;  
  5.     }  
  6.     public string Name {  
  7.         get;  
  8.         set;  
  9.     }  
  10.     public string Address {  
  11.         get;  
  12.         set;  
  13.     }  
  14.     public void Print() {  
  15.         WriteLine($ "Customer Id is: {CustomerId.Length} character(s) long");  
  16.         WriteLine($ "Customer name : {Name.ToUpper()}");  
  17.         WriteLine($ "Address : {Address.Trim()}");  
  18.     }  
  19. }   
Now from the “Main()” method of a console app, create the instance of Customer class and call the Print() method without initializing the properties of customer class.
  1. static void Main()    
  2. {    
  3.    Customer customer = new Customer();  
  4.    customer.Print();    
  5. }  
NullReferenceException-Example 5
 
As we can see that the code was compiled successfully, but it is throwing the “NullReferenceException” exception at run time.
 
It would be better if we have enabled the '#nullable' context, so that Visual studio can give us warning, and we can correct the code at compile-time, which helps in reducing run-time exceptions.
 
Use the same steps as discussed earlier to enable the '#nullable' context and enable it for the current project.
 
Now we can see in the screenshot below that it is giving warning messages.
 
nullable annotations context warning -2
 
Let’s fix it.
 
For demo purposes right now, assume that all the 3 properties can be null and so mark it as nullable properties intentionally.
  1. public string? CustomerId { getset; }    
  2. public string? Name { getset; }    
  3. public string? Address { getset; }  
Now we can see that those warning messages disappear, but some new warning messages get appeared.

nullable annotations context warning -3
 
The above warning messages are evident because we are using “possible null reference” instances without checking for null values.
 
Now replace the code of the “Print()” method with the below line of codes. 
  1. public void Print()  
  2.         {  
  3.             WriteLine($"Customer Id is: {CustomerId?.Length??0} character(s) long");  
  4.             WriteLine($"Customer name : {Name?.ToUpper() ?? "N/A"}");  
  5.             WriteLine($"Address : {Address?.Trim() ?? "N/A"}");  
  6.         }  
Complete code
  1. using static System.Console;  
  2.   
  3. namespace NonNullableExample2  
  4. {  
  5.     class Program  
  6.     {  
  7.         static void Main()  
  8.         {  
  9.             Customer customer = new Customer();  
  10.             customer.Print();  
  11.         }  
  12.     }  
  13.   
  14.     class Customer  
  15.     {  
  16.         public string? CustomerId { getset; }  
  17.         public string? Name { getset; }  
  18.         public string? Address { getset; }  
  19.         public void Print()  
  20.         {  
  21.             WriteLine($"Customer Id is: {CustomerId?.Length??0} character(s) long");  
  22.             WriteLine($"Customer name : {Name?.ToUpper() ?? "N/A"}");  
  23.             WriteLine($"Address : {Address?.Trim() ?? "N/A"}");  
  24.         }  
  25.     }  
  26. }  
Output
 
Output Example 2
 
Now we can see how clear the code we have written. We have also provided default print value in case the property is null. Like for numeric type, we have provided value zero(0), for the string, we have provided “N/A” as default value.
 

What happens if we use a null check with a non-nullable reference type in the '#nullable' context?

 
Null check with a non-nullable reference does not give you any warning.
 

Does it stop us from assigning a null value to a “non-nullable” reference type?

 
No, by default, it does not stop you from assigning a null value to a non-nullable reference type.
 
Have a look at the below code 
  1. using static System.Console;  
  2.   
  3. namespace NonNullableExample2  
  4. {  
  5.     class Program  
  6.     {  
  7.         static void Main()  
  8.         {  
  9.             Customer customer = new Customer { CustomerId = null };  
  10.             customer.Print();  
  11.         }  
  12.     }  
  13.   
  14.     class Customer  
  15.     {  
  16.         public Customer() => CustomerId = "-1";  
  17.         public string CustomerId { getset; }    
  18.  //Omitted for brevity        
  19.         public void Print()  
  20.         {  
  21.             WriteLine($"Customer Id is: {CustomerId?.Length} character(s) long");   
  22.      //Omitted for brevity             
  23.         }  
  24.     }  
  25. }  
 In the above code, we can see that I am marking “CustomerId” property as a non-nullable type, but still, we can assign a null value.
  1. Customer customer = new Customer { CustomerId = null };  
So far, we have discussed the basic scenario of “nullable” & “non-nullable” reference types in the '#nullable' context.
 
There are many more scenarios for “nullable” & “non-nullable” reference types in the '#nullable' context, which can explore by ourselves while using it. Discussing all those scenarios is not possible in this article; otherwise, it will become a very long article.
 

What is the “Nullable Enable” concept in C# 8.0

 
Enabling the '#nullable' annotations context is also known as the “Nullable Enable” concept in C# 8.0. And it can be done at the following levels,
  1. At project level
  2. At Class level
  3. At Block Level

Enabling '#nullable' annotations context at file or class level

 
We have already seen how we can enable the '#nullable' annotations context at the project level. Let remove it from the project level and move to class level.
 
Go to the project and remove the below line of code from there. 
  1. <Nullable>enable</Nullable>  
Project file code after removing nullable enable 
  1. <Project Sdk="Microsoft.NET.Sdk">  
  2.   <PropertyGroup>  
  3.     <OutputType>Exe</OutputType>  
  4.     <TargetFramework>netcoreapp3.1</TargetFramework>  
  5.   </PropertyGroup>  
  6. </Project>  
Now go to the class file and enable it at the file level. Just add the below line at the class level.
 
#nullableenable
 
Following is the code snippet of the same. 
  1. using static System.Console;  
  2. #nullable enable  
  3.   
  4. namespace NonNullableExample2  
  5. {  
  6.     class Program  
  7.     {  
  8.         //Omitted for brevity  
  9.     }  
  10.   
  11.     class Customer  
  12.     {  
  13.         //Omitted for brevity  
  14.     }  
  15. }  
As we have kept it at the top level, so it has been applied for both the classes in the file. 
 

Enabling '#nullable' annotations context at a block-level

 
Syntax
  1. #nullable enable    
  2. //Our code block for which we would like to enable #nullable annotation context 
  3. #nullable disable  
 Below is a code snippet for the same.
  1. //Omitted for brevity  
  2. namespace NonNullableExample2  
  3. {  
  4.     class Program  
  5.     {  
  6.         //Omitted for brevity  
  7.     }  
  8.   
  9.     class Customer  
  10.     {  
  11.         //Omitted for brevity  
  12.         public string CustomerId { getset; }  
  13.         public string? Name { getset; }  
  14. #nullable enable  
  15.         public string? Address { getset; }  
  16. #nullable disable  
  17.         public void Print()  
  18.         {  
  19.             //Omitted for brevity  
  20.         }  
  21.     }  
  22. }  
Note
If we use the syntaxes outside the block, then it gives us a warning message for the same. The following is a screenshot explaining the same.
 
nullable annotations context warning -4
 

Null-forgiving operator (‘!’)

 
Null-forgiving operator (‘!’) is available in C# 8.0 and later versions. This operator is a unary operator. It is used as a postfix.
 
In-fact ‘!’ is a logical negation operator, but there are some differences. If the operator ‘!’ is used as a prefix, then it is “logical negation operator”, and if it is used at postfix, then it is “null-forgiving operator”.
 
The Null-forgiving operator is compiling time only and does not have any effect at the run time. i.e., the expression “x!” and “x” are the same at the run time.
 
To understand it in a better way, let’s take the same class example which we are using earlier. 
  1. class Customer  
  2.     {  
  3. #nullable enable  
  4.         public string? Name { getset; }  
  5.         public string? Address { getset; }  
  6.         public void Print()  
  7.         {  
  8.             WriteLine($"Customer name : {Name.ToUpper()}");  
  9.             WriteLine($"Address : {Address?.Trim() ?? "N/A"}");  
  10.         }  
  11.     }  
Just compile the above class, and we can see a warning message as follows,
 
Warning CS8602: Dereference of a possibly null reference.
 
nullable annotations context warning -5
 
But if we are confident enough and know that the “Name” property cannot be null at this context so we can use “Null-forgiving operator” to get rid of the warning.  
  1. class Customer  
  2.     {  
  3. #nullable enable  
  4.         public string? Name { getset; }  
  5.         public string? Address { getset; }  
  6.         public void Print()  
  7.         {  
  8.             WriteLine($"Customer name : {Name!.ToUpper()}");  
  9.             WriteLine($"Address : {Address?.Trim() ?? "N/A"}");  
  10.         }  
  11.     }  
There are some other uses of this operator as well. We can use it for Unit test cases as well.

So far, we have covered the null handling feature available till C# 8.0 version. Now let’s have a brief look at the proposed and upcoming feature in C# 9.0, which are related to null handling.
 

Simplified parameter null validation (proposed in C# 9.0)

 
In C# 8.0
  1. void AddCustomer(Customer customer)  
  2.         {  
  3.             if (customer is null)  
  4.             {  
  5.                 throw new ArgumentNullException(nameof(customer));  
  6.             }  
  7.             _customers.Add(customer);  
  8.         }  
Proposed in C# 9.0
  1. void AddCustomer(Customer customer!) => _customers.Add(customer);  

Target typed null coalescing (`??`) expression (proposed in C# 9.0)

 
In C# 8.0 the following code snippet is allowed,
  1. List<int> list = new List<int>();  
  2. IEnumerable<int> list2 = list;  
And the following code snippet is also allowed in C# 8.0.
  1. List<int> list = new List<int>();  
  2. IEnumerable<int> list2 = Array.Empty<int>();  
But the below code snippet not allowed in C# 8.0.
  1. List<int> list = new List<int>();  
  2. IEnumerable<int> list2 = list ?? Array.Empty<int>();  
Proposed in C# 9

The below code snippet should be allowed
  1. List<int> list = new List<int>();  
  2. IEnumerable<int> list2 = list ?? Array.Empty<int>();   

Nullable-enhanced common type pasting (proposed in C# 9.0)

 
This proposed feature is similar to “Target typed null coalescing (`??`) expression” feature.
 
In C# 8.0.
  1. bool expression = true;  
  2. int? x = expression ? 1 :(int?)null;  
 Proposed in C# 9.0,
  1. bool expression = true;  
  2. int? x = expression ? 1 : null;  

Ternary operation with nullable int and double pasting (proposed in C# 9.0)

 
In C# 8.0 the following code will not work,
  1. bool expession = true;  
  2. int? x = 1;  
  3. double y = 2.0;  
  4. var z = expession ? x : y;  

Compilation Error in C# 8.0

 
Error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'int?' and 'double.'
 
In C# 9.0, the above code snippet is proposed to compile, and this proposed feature is referred to as “Ternary operation with nullable int and double.”
 
Note
some of the proposed features are similar and may get merged into a single feature at the time of the official release of C# 9.0. 
 

Summary

 
In this article, we have covered the following topics
  1. Nullable value type (‘nullable<value type>’ or ‘value type?’)
  2. Null Coalescing Operator (‘??’)
  3. Null conditional operator for member access (‘?.’)
  4. Null conditional operator for index-based access (‘?[]’)
  5. Null-coalescing assignment (‘??=’)
  6. Non-nullable Reference Type with '#nullable' annotations context
  7. Nullable reference Type (‘reference type?’) with '#nullable' annotations context
  8. Enabling '#nullable' annotations context at the project level
  9. Enabling '#nullable' annotations context at class or block-level
  10. Null-forgiving operator (‘!’) 
  11. Simplified parameter null validation
  12. Target typed null coalescing (`??`) expression
  13. Nullable-enhanced common type
  14. Ternary operation with nullable int and double