C# 8.0 Features (Part 3)

Part 2

  1. Using Declarations
  2. Nullable Reference Types
  3. Asynchronous Enumerables

1. Using Declarations

C# 8.0 introduces an alternative way to leverage using statements. This new way is called a using declaration. It reduces unnecessary code nesting and improves readability.

Here is a C# 7 method which prints each line of a text file to the console:

public static void PrintToConsole(string filePath)
{
    using (var reader = new StreamReader(filePath))
    {
        while (!reader.EndOfStream)
        {
            Console.WriteLine(reader.ReadLine());
        }
    }
}

The above code can be modified to leverage C# 8.0 using declarations like so:

public static void PrintToConsoleV2(string filePath)
{
    using var reader = new StreamReader(filePath);
    while (!reader.EndOfStream)
    {
        Console.WriteLine(reader.ReadLine());
    }
}

The StreamReader will be disposed at the end of the scope in which the using declaration is made. In this case, the end of the scope is the end of the method.

2. Nullable Reference Types

One of the most frequent mistakes made by C# programmers is not properly accounting for null references. C# 8.0 offers a way to largely eliminate null-reference exceptions.

Nullable reference types can be enabled by adding the following xml element to the main PropertyGroup element of the csproj file:

<Nullable>enable</Nullable>

After adding this to the csproj file of a new .NET Core 3.1 console app, it looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

Let’s take a look at an example where this C# 8.0 feature can help us avoid a null-reference exception. Here are a couple of simple classes that we will use to demonstrate:

public class Author
{
    public string? Name { get; set; }
}

public class Book
{
    public string? Title { get; set; }
    public Author? Author { get; set; }
}

You may have noticed something strange in the above classes. Each of the properties’ types are suffixed with ?. The ? indicates that null is an acceptable value for that property. If we don’t suffix the properties’ types with ? we will get a compiler warning indicating that the class can be initialized with null values for the properties.

Consider the following program:

static void Main(string[] args)
{
    var book = new Book();
    Console.WriteLine(book.Author.Name);
}

In Visual Studio, book.Author should be underlined to indicate a compiler warning. C# 8.0 has correctly warned us of an impending null reference exception.

We can easily resolve this compiler warning by making use of the null-conditional operator which was introduced in C# 6

static void Main(string[] args)
{
    var book = new Book();
    Console.WriteLine(book.Author?.Name);
}

3. Asynchronous Enumerables

C# 8.0 allows us to asynchronously iterate over an enumerable. We can do this by using the new IAsyncEnumerable type. Rather than having to load every element in the enumerable into memory at once, we can load each element as we need to.

Here is a simple method which simulates an asynchronous data source:

static async IAsyncEnumerable<string> GetData()
{
    var dataPoints = new List<string> {
        "point1",
        "point2",
        "point3" 
    };
    foreach (var dataPoint in dataPoints)
    {
        await Task.Delay(1000);
        yield return dataPoint;
    }
}

We can use a foreach loop to consume the IAsyncEnumerable :

static async Task Main(string[] args)
{
    await foreach (var dataPoint in GetData())
    {
        Console.WriteLine(dataPoint);
    }
}
Tags:,