À peine commençons nous à utiliser pleinement l’ensemble des apports de C# 8.0, et voilà que les premières améliorations de C# 9 pointent le bout de leur nez ! Voyons-en quelques unes en détail.

Top-level programs

Jusqu’à maintenant, pour écrire un programme même le plus simple, des Hello World nécessitaient un peu de code, ce qui pouvait sembler inutile :

using System;
class Program
{
    static void Main()
    {
        Console.WriteLine("Hello World!");
    }
}

Cela pouvait dérouter les débutants, notamment ceux venant de Python où il est possible d’écrire simplement : print ‘Hello World!. Il est désormais possible de faire aussi simple avec C# 9 : 

System.Console.WriteLine("Hello World!");

Méthodes partielles

Il est déjà possible de découper une classe en plusieurs fichiers avec partial class. C# 9 apporte la possibilité d’avoir des méthodes partielles, c’est-à-dire déclarer une méthode dans un fichier, un peu à la manière des virtual, et de fournir l’implémentation de cette méthode dans un autre fichier !

interface IStudent
{
    string GetName();
}

partial class C : IStudent
{
    public virtual partial string GetName(); 
}

partial class C
{
    public virtual partial string GetName() => "Jarde";
}

Covariant return

Qui n’a pas déjà été confronté à cette problématique : retourner, dans une implémentation d’une virtual ou abstract, un type plus précis que dans la déclaration de base ? Eh bien ce sera possible avec C# 9 !

abstract class Animal
{
    public abstract Food GetFood();
}

class Tiger : Animal
{
    public override Meat GetFood() => ...;
}

Accesseurs d’init uniquement

En plus du get et set, un nouveau type d’accesseur init fait son entrée. A l’instar du set, il est utilisable pour définir une valeur à une propriété, mais uniquement lors de l’initialisation d’un objet.

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

Il est donc toujours possible d’écrire le code suivant :

// setting the properties on object initialization => OK
var person = new Person
{
    FirstName = "Rick",
    LastName = "Hunter"
};

// setting property after init => KO
person.LastName = "Morty";

Records

Ces accesseurs d’init sont utiles pour les propriétés d’un objet. Maintenant si vous souhaitez rendre tout un objet immuable, voici venu le record, par l’ajout du mot clef data.

public data class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public string Address { get; init; }
}

Il faut voir ces objets comme étant des types valeurs complexes, dont aucune des valeurs ne bouge : un changement impliquant de créer un nouvel objet (à l’instar des string par exemple).

Vient également d’autres comportements, comme celui de pouvoir écrire de manière courte le même record. 

public data class Person { string FirstName; string LastName; string Address; }

Les deux champs seront déclarés comme précédemment, en public, avec un getter et un accesseur d’init. La première déclaration et celle juste au-dessus sont identiques !

Création de nouveau record

La modification d’un immuable impliquant de créer une nouvelle valeur à partir d’une existante. Par exemple le changement de l’adresse de notre personne n’est rien d’autre qu’une copie de l’objet, avec une autre valeur pour l’adresse. Notre objet représente une personne à un moment donné. Pour simplifier ce processus de recopie/création, le mot clef with peut être utilisé :

var persoWithOtherAddress = person with { Address = "some where else" };

Cela créera un nouveau record reprenant les valeurs du précédent, en utilisant uniquement la nouvelle valeur de la propriété Address. Il est possible de spécifier autant de propriétés que souhaité.

Typage implicite amélioré

Le typage implicite, ou le target typing correspond au moment où le type d’une expression est dérivé du contexte dans lequel elle est utilisée. Par exemple, les expression lambda ont toujours un typage implicite.

Dans C# 9, il y a de nouvelles expressions qui supportent le typage implicite.

Expressions new

Précédemment les expressions new devaient toujours préciser le type (à part les constructions de tableaux). Maintenant on peut s’en passer !

Point p = new (4, 2);

?: et ??

Il arrivait parfois que dans les expressions conditionnelles ?: et ?? le type ne pouvait pas être détecté implicitement par le compilateur et nous forçait à ajouter un cast. C’est désormais moins le cas :

int? result = test ? 42 : null;
Person p = student ?? customer; // both share the same Person base type

Et bien d’autres…

  • Pattern matching amélioré
  • D’autres opérations sur les records (égalité, héritage…)

Sources : .net blog et Language Feature Status

Plus d’articles :

Le paiement mobile

Le paiement mobile

Quels sont les pros et les cons de ce nouveau mode de paiement ? Quelles sont les technologies d’intelligences artificielles derrière cette révolution de paiement ? Quels enjeux envisage-t-on dans un futur proche ?