Five new features of C# 10

C#'s GitHub page contains a long list of tempting ideas, some of which are still under discussion. If you want to know what new features are included in C# 10, you can wait for the release of the new version in November. Alternatively, you can focus on the c# team's favorite features. At the recent Microsoft Build conference, Mads Torgersen, the chief designer of c#, revealed some of the ongoing work. Here are five new features that will be available in the next version of the language.

1. global using

C #'s source code file usually imports a bunch of namespaces at the beginning. The following is an ordinary ASP Net web application code snippet:

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 is nothing special about the way this code is written. Previously, namespace import allowed us to quickly understand which libraries a class was using. Now, however, it's just a pile of code that has to be written and no one sees.

C# 10 introduces a new pattern that allows you to define the namespace import of the entire project using the keyword global. The recommended practice is to place the global import in a separate file (one for each project), which can be named usings.cs or imports.cs. The contents are roughly as follows:

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 you can simplify the original file:

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

Visual Studio highlights duplicate namespaces (that is, namespaces imported in both global and local files). Although this is not an error, removing duplicate namespaces can reduce the amount of code and focus on the special namespace that a particular file is using.

2. File scope namespace

C# 10 provides another way to simplify code: declare file scoped namespaces. File scoped namespaces are automatically applied to the entire file without indenting.

In other words, the following wording:

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

Can become:

namespace LoggingTestApp;
public class Startup
{
    ...
}

If you add another namespace block to a file that uses a file scope namespace, a nested namespace is created:

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

C# designers believe that this change can clean up the waste of horizontal space (just as global using cleans up the waste of vertical space). The overall goal is to make the code shorter, narrower and more concise. However, these changes can also reduce the difficulty for novices to learn c#. Combined with global using and file range namespace, a Hello World console application can be created in a few lines of code.

3. Null parameter check

In the spirit of reducing template code, C# provides a very good new function: null parameter check. You must have written a method that needs to check null values. For example, the following code:

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

Now, you just need to add "!!!" at the end of the parameter name, C # will automatically add this null parameter check. The above code can be simplified to:

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

Now, if you pass a null value to Address, you will automatically throw ArgumentNullException.

This detail may seem trivial, but in fact it is a very simple and valuable way to optimize the language. A large number of studies show that the cause of program errors is often due to the repeated occurrence of errors that are very easy to avoid, not because the concepts in the code are too complex, but because reading the code is very tired and human attention is limited. Reducing the amount of code can reduce the time required to review the code, the cognitive load required to process the code, and the possibility of ignoring some errors due to reduced attention.

4. required attribute

Previously, we could only use class constructors to ensure that objects were created correctly. Nowadays, we often use more lightweight structures, such as the automatically implemented attributes in the following records:

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 may use the object initialization syntax:

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

But what if some properties in your object are required? You can add a constructor as before, but you need to add more template code. In addition, copying values from a parameter to an attribute is another easy to understand but common error.

The keyword required introduced in C# 10 can eliminate such problems:

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

As a result, you cannot create an Employee without setting the Name property.

5. Keyword field

Over the years, the C# team has made great efforts to simplify the code by automatically implementing attributes. The above Employee record is a good example. It declares three immutable attributes using the get and init keywords. The data is stored in three private fields, but these fields are created automatically without human intervention. And you'll never see these fields.

Auto implemented properties are great, but their role is limited to this. When auto implemented properties cannot be used, you must add supporting fields to the class and write normal property methods, just like returning to C# 2. However, C# 10 provides a keyword field, which can automatically create support fields.

For example, suppose you want to create a record to process the initial attribute values. In the following code, we made some modifications to the Employee class to ensure that the HiredDate field only contains date information from the DateTime object (excluding time information):

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 neat, simple, and close to declarative.

You can use the keyword field to access fields in get, set, or init. Moreover, you may need to verify a property, just like verifying a property in a normal class:

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 verify automatically implemented properties:

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

In essence, as long as you do not need to modify the data type of the attribute, you do not need to declare the supporting fields yourself.

6. Summary

Of course, there must be more than five new features in C# 10. There are also some changes in expressions, as well as a controversial change: defining static members in interfaces. We have to wait patiently.

Overall, the development focus of C# 10 is very clear, that is, reducing the amount of code, providing more convenience and reducing the burden on developers.

Added by ndondo on Wed, 15 Dec 2021 14:43:40 +0200