Book Image

C# 9 and .NET 5 – Modern Cross-Platform Development - Fifth Edition

By : Mark J. Price
Book Image

C# 9 and .NET 5 – Modern Cross-Platform Development - Fifth Edition

By: Mark J. Price

Overview of this book

In C# 9 and .NET 5 – Modern Cross-Platform Development, Fifth Edition, expert teacher Mark J. Price gives you everything you need to start programming C# applications. This latest edition uses the popular Visual Studio Code editor to work across all major operating systems. It is fully updated and expanded with a new chapter on the Microsoft Blazor framework. The book’s first part teaches the fundamentals of C#, including object-oriented programming and new C# 9 features such as top-level programs, target-typed new object instantiation, and immutable types using the record keyword. Part 2 covers the .NET APIs, for performing tasks like managing and querying data, monitoring and improving performance, and working with the file system, async streams, serialization, and encryption. Part 3 provides examples of cross-platform apps you can build and deploy, such as websites and services using ASP.NET Core or mobile apps using Xamarin.Forms. The best type of application for learning the C# language constructs and many of the .NET libraries is one that does not distract with unnecessary application code. For that reason, the C# and .NET topics covered in Chapters 1 to 13 feature console applications. In Chapters 14 to 20, having mastered the basics of the language and libraries, you will build practical applications using ASP.NET Core, Model-View-Controller (MVC), and Blazor. By the end of the book, you will have acquired the understanding and skills you need to use C# 9 and .NET 5 to create websites, services, and mobile apps.
Table of Contents (23 chapters)
22
Index

Working with null values

You have now seen how to store primitive values like numbers in variables. But what if a variable does not yet have a value? How can we indicate that? C# has the concept of a null value, which can be used to indicate that a variable has not been set.

Making a value type nullable

By default, value types like int and DateTime must always have a value, hence their name. Sometimes, for example, when reading values stored in a database that allows empty, missing, or null values, it is convenient to allow a value type to be null. We call this a nullable value type.

You can enable this by adding a question mark as a suffix to the type when declaring a variable. Let's see an example:

  1. In the Chapter02 folder, create a new folder named NullHandling.
  2. Add the NullHandling folder to the Chapter02 workspace.
  3. Create a new Terminal window for the NullHandling project.
  4. Create a new console application project in the NullHandling folder.
  5. Select NullHandling as the current project for OmniSharp.
  6. In the NullHandling project, in Program.cs, in the Main method, add statements to declare and assign values, including null, to int variables, as shown in the following code:
    int thisCannotBeNull = 4;
    thisCannotBeNull = null; // compile error!
    int? thisCouldBeNull = null;
    Console.WriteLine(thisCouldBeNull);
    Console.WriteLine(thisCouldBeNull.GetValueOrDefault());
    thisCouldBeNull = 7;
    Console.WriteLine(thisCouldBeNull);
    Console.WriteLine(thisCouldBeNull.GetValueOrDefault());
    
  7. Comment out the statement that gives a compile error.
  8. Run the application and view the result, as shown in the following output:
    0
    7
    7
    

The first line is blank because it is outputting the null value!

Understanding nullable reference types

The use of the null value is so common, in so many languages, that many experienced programmers never question the need for its existence. But there are many scenarios where we could write better, simpler code if a variable is not allowed to have a null value.

More Information: You can find out more through the following link, where the inventor of null, Sir Charles Antony Richard Hoare, admits his mistake in a recorded hour-long talk: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

The most significant change to the language in C# 8.0 was the introduction of nullable and non-nullable reference types. "But wait!", you are probably thinking, "Reference types are already nullable!"

And you would be right, but in C# 8.0 and later, reference types can be configured to no longer allow the null value by setting a file- or project-level option to enable this useful new feature. Since this is a big change for C#, Microsoft decided to make the feature opt-in.

It will take multiple years for this new C# language feature to make an impact since there are thousands of existing library packages and apps that will expect the old behavior. Even Microsoft has not had time to fully implement this new feature in all the main .NET 5 packages.

More Information: You can read the tweet about achieving 80% annotations in .NET 5 at the following link: https://twitter.com/terrajobst/status/1296566363880742917

During the transition, you can choose between several approaches for your own projects:

  • Default: No changes are needed. Non-nullable reference types are not supported.
  • Opt-in project, opt-out files: Enable the feature at the project level and, for any files that need to remain compatible with old behavior, opt out. This is the approach Microsoft is using internally while it updates its own packages to use this new feature.
  • Opt-in files: Only enable the feature for individual files.

Enabling nullable and non-nullable reference types

To enable the feature at the project level, add the following to your project file:

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

To disable the feature at the file level, add the following to the top of a code file:

#nullable disable

To enable the feature at the file level, add the following to the top of a code file:

#nullable enable

Declaring non-nullable variables and parameters

If you enable nullable reference types and you want a reference type to be assigned the null value, then you will have to use the same syntax as making a value type nullable, that is, adding a ? symbol after the type declaration.

So, how do nullable reference types work? Let's look at an example. When storing information about an address, you might want to force a value for the street, city, and region, but the building can be left blank, that is, null:

  1. In NullHandling.csproj, add an element to enable nullable reference types, as shown highlighted in the following markup:
    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
        <Nullable>enable</Nullable>
      </PropertyGroup>
    </Project>
    
  2. In Program.cs, at the top of the file add a statement to enable nullable reference types, as shown in the following code:
    #nullable enable
    
  3. In Program.cs, in the NullHandling namespace, above the Program class, add statements to declare an Address class with four fields, as shown in the following code:
    class Address
    {
      public string? Building;
      public string Street;
      public string City; 
      public string Region;
    }
    
  4. After a few seconds, note that the C# extension warns of problems with non-nullable fields like Street, as shown in the following screenshot:
    A screenshot of a cell phone  Description automatically generated

    Figure 2.6: Warning messages about non-nullable fields in the PROBLEMS window

  5. Assign the empty string value to each of the three fields that are non-nullable, as shown in the following code:
    public string Street = string.Empty;
    public string City = string.Empty;
    public string Region = string.Empty;
    
  6. In Main, add statements to instantiate an Address and set its properties, as shown in the following code:
    var address = new Address();
    address.Building = null;
    address.Street = null;
    address.City = "London";
    address.Region = null;
    
  7. Note the warnings, as shown in the following screenshot:
    A screenshot of a cell phone  Description automatically generated

    Figure 2.7: Warning message about assigning null to a non-nullable field

So, this is why the new language feature is named nullable reference types. Starting with C# 8.0, unadorned reference types can become non-nullable, and the same syntax is used to make a reference type nullable as is used for value types.

More Information: You can watch a video to learn how to get rid of null reference exceptions forever at the following link: https://channel9.msdn.com/Shows/On-NET/This-is-how-you-get-rid-of-null-reference-exceptions-forever

Checking for null

Checking whether a nullable reference type or nullable value type variable currently contains null is important because if you do not, a NullReferenceException can be thrown, which results in an error. You should check for a null value before using a nullable variable, as shown in the following code:

// check that the variable is not null before using it
if (thisCouldBeNull != null)
{
  // access a member of thisCouldBeNull
  int length = thisCouldBeNull.Length; // could throw exception
  ...
}

If you are trying to use a member of a variable that might be null, use the null-conditional operator ?., as shown in the following code:

string authorName = null;
// the following throws a NullReferenceException
int x = authorName.Length;
// instead of throwing an exception, null is assigned to y
int? y = authorName?.Length;

More Information: You can read more about the null-conditional operator at the following link: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators

Sometimes you want to either assign a variable to a result or use an alternative value, such as 3, if the variable is null. You do this using the null-coalescing operator, ??, as shown in the following code:

// result will be 3 if authorName?.Length is null
var result = authorName?.Length ?? 3; 
Console.WriteLine(result);

More Information: You can read about the null-coalescing operator at the following link: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator