Top 5 Must-Know New Features in C# 10

Top 5 Must-Know New Features in C# 10

The C# GitHub page lists a long list of enticing ideas, and some troublesome issues are still under discussion.

Last updated 12/12/2021 10:01 AM
Matthew MacDonald
8 min read
Category
.NET
Tags
.NET C# GitHub C# 10

The C# GitHub page lists a long list of enticing ideas, some of which are still under discussion with regard to thorny issues. If you want to know which new features are actually included in C# 10, you can wait for the new version in November. Alternatively, you can follow the C# team's showcase of their favorite features. At the recent Microsoft Build conference, C# lead designer Mads Torgersen revealed some ongoing work. Here are the top five new features that the next version of the language will provide.

1. global using

C# source code files typically start with a bunch of namespace imports. Below is a code snippet from a common ASP.NET web application:

using LoggingTestApp.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace LoggingTestApp
{
	public class Startup
    {
        ...
    }
}

There's nothing special about this code. Previously, namespace imports gave a quick idea of what libraries a class was using. Nowadays, however, this is just boilerplate code that has to be written but no one reads.

C# 10 introduces a new mode that allows you to define namespace imports for the entire project using the keyword global. The recommended practice is to place global imports in a separate file (one per project), which can be named usings.cs or imports.cs. The content would be roughly:

global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Hosting;
global using Microsoft.AspNetCore.HttpsPolicy;
global using Microsoft.AspNetCore.Identity;
global using Microsoft.AspNetCore.Identity.UI;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;

Then the original file can be simplified to:

using LoggingTestApp.Data;
using Serilog;
namespace LoggingTestApp
{
	public class Startup
    {
        ...
    }
}

Visual Studio will highlight duplicate namespaces (those imported both globally and locally). While this is not an error, removing duplicate namespaces reduces code volume and focuses attention on the special namespaces used by a specific file.

2. File-scoped namespaces

C# 10 offers another way to simplify code: declaring file-scoped namespaces. A file-scoped namespace automatically applies to the entire file and requires no indentation.

In other words, this:

namespace LoggingTestApp
{
	public class Startup
    {
        ...
    }
}

can become:

namespace LoggingTestApp;
public class Startup
{
    ...
}

If you add another namespace block in a file that already uses a file-scoped namespace, it creates a nested namespace:

namespace Company.Product;
// This block creates the namespace Company.Product.Component
namespace Component
{
}

The C# designers believe this change cleans up wasted horizontal space (just as global using cleans up wasted vertical space). The overall goal is to make code shorter, narrower, and more concise. But these changes can also lower the learning curve for newcomers to C#. Combining global using with file-scoped namespaces, you can create a Hello World console application in just a few lines of code.

3. Null-parameter checking

In the spirit of reducing boilerplate code, C# 10 provides a very nice new feature: null-parameter checking. You've definitely written methods that need to check for null values. For example, consider this code:

public UpdateAddress(int personId, Address newAddress)
{
	if (newAddress == null)
    {
		throw new ArgumentNullException("newAddress");
    }
    ...
}

Now, you can simply append !! to the parameter name, and C# will automatically add this null-parameter check. The above code can be simplified to:

public UpdateAddress(int personId, Address newAddress!!)
{
    ...
}

Now, if a null value is passed to newAddress, an ArgumentNullException is automatically thrown.

This detail may seem trivial, but it's actually a very simple and valuable way to optimize the language. A large amount of research shows that program errors often occur because of easily avoidable mistakes that happen repeatedly—not because the concepts in the code are too complex, but because reading code is tiring and human attention is limited. Reducing the amount of code can reduce the time needed to review code, the cognitive load required to process it, and the likelihood of overlooking errors due to diminished attention.

4. The required property

Previously, we could only rely on class constructors to ensure objects were created correctly. Nowadays, we often use lighter-weight structures, such as automatically implemented properties in records like the one below:

public record Employee
{
    public string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init; }
}

When creating instances of such lightweight objects, we might use object initializer syntax:

var theNewGuy = new Employee
{
    Name = "Dave Bowman",
    YearlySalary = 100000m,
    HiredDate = DateTime.Now()
};

But what if some properties in your object are required? You could add a constructor as before, but that adds more boilerplate code. Additionally, copying values from parameters to properties is another common but easy-to-understand source of mistakes.

C# 10 introduces the keyword required to eliminate this problem:

public record Employee
{
    public required string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init; }
}

Now, an Employee cannot be created without setting the Name property.

5. The field keyword

For years, the C# team has put a lot of effort into simplifying code through automatically implemented properties. The Employee record above is a great example, using get and init keywords to declare three immutable properties. The data is stored in three private fields, but these fields are created automatically without manual intervention. And you never see these fields.

Auto-implemented properties are great, but their usefulness is limited. When auto-implemented properties can't be used, you have to add backing fields to the class and write regular property methods, going back to the C# 2 style. However, C# 10 introduces the keyword field, which automatically creates the backing field.

For example, suppose you want to create a record that handles initial property values. In the following code, we modify the Employee class to ensure that the HiredDate field contains only date information from the DateTime object (no time component):

public record Employee
{
    public required string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init => field = value.Date(); }
}

This code is very clean, simple, and close to declarative.

You can use the field keyword to access fields within get, set, or init. You may need to validate a property, just like validating properties in regular classes:

private string _firstName;
public string FirstName
{
    get
    {
        return _firstName;
    }
    set
    {
        if (value.Trim() == "")
            throw new ArgumentException("No blank strings");
        _firstName = value;
    }
}

You can use field to validate an auto-implemented property:

public string FirstName {get;
    set
    {
        if (value.Trim() == "")
            throw new ArgumentException("No blank strings");
        field = value;
    }
}

Essentially, as long as you don't need to change the property's data type, you don't have to declare a backing field yourself.

6. Summary

Of course, there are more than five new features in C# 10. There are also some expression-level changes and a controversial change: defining static members in interfaces. We'll have to wait patiently.

Overall, the development focus of C# 10 is clear: reduce code volume, provide more convenience, and lighten the burden on developers.

Author: Matthew MacDonald

Original link: https://medium.com/young-coder/a-closer-look-at-5-new-features-in-c-10-f99738b0158e

Translator: Wanyue

Editor: Ouyang Shuli

Produced by: CSDN (ID: CSDNnews)

Statement: This article was translated by CSDN. Please indicate the source when reprinting.

Keep Exploring

Related Reading

More Articles