Skip to content

Les nouveautés de C# 7.0

Posted on:9 mai 2017 at 02:00

Le langage de programmation C# est maintenant disponible dans une nouvelle monture ! La version 7.0 a profité de la sortie de Visual Studio 2017 pour faire son apparition et avec elle, quelques changements bien sympathiques ! Je précise que cette liste ne sera pas exhaustive et ne comportera que les principaux changements. À l’image de l’article sur la norme ES6 du JavaScript.

Mise en contexte

Mais avant, petit tour rapide du langage C#. Celui-ci est développé est édité par Microsoft depuis quelques années. Ce qui fait sa puissance est surtout sa modularité car, le langage peut être autant utilisé pour faire un site internet (ASP.Net) qu’une application avec fenêtre (Winform, WPF) qu’un système IOT (Gadgeteer, Raspberry Pi) ou qu’une application Mobile pour IOS, Android ou encore Windows Mobile (avec Xamarin).

Sans parler d’application holographique (Hololens), Xbox One, serveur (pour Linux, Windows ou Mac OS) ou même en tant que langage de Script avec le moteur de jeu Unity ou encore le CryEngine. Vous l’aurez compris, C# est partout !

Les nouveautés

Attaquons maintenant le vif du sujet, C # 7.0 ajoute un certain nombre de nouvelles fonctionnalités et met l’accent sur la façon de gérer des données, la simplification ainsi que la performance générale. L’amélioration de la visibilité de la syntaxe est aussi de la partie. L’objectif est clair, vous rendre, le développeur, plus productif tout en étant plus clair et performant. Que demander de plus ?

Amélioration de la syntaxe

Commençons simplement avec un petit ajout bien sympathique, certes anecdotique mais, qui est toujours agréable, le séparateur de chiffres. Celui-ci vous permettra de rendre visuellement plus agréable la lecture d’un nombre dans le code. Que ce soi du binaire, de l’hexadécimal ou autre.

var a = 0xAB_CD_EF;
var b = 0b1010_1011_1100_1101_1110_1111;
var c = 123_456;

Cela n’influence donc en aucun cas la valeur. Mais améliore grandement la lisibilité de la variable pour un être humain.

Les variables “Out”

Cette nouvelle fonctionnalité a pour objectif de simplifier un tantinet la syntaxe lors de l’utilisation de out lors de l’appel d’une fonction. Dans la version précédente du langage C#, il fallait pré-déclarer les variables en avance. Ceci n’est maintenant plus nécessaire, un exemple vaut mieux qu’un long discours. (Je précise que la plupart des exemples sont tirés de la documentation officielle).

// Anciennement
public void PrintCoordinates(Point p)
{
     int x, y; // On prédéclare les variables
     p.GetCoordinates(out x, out y);
     WriteLine($"({x}, {y})");
}

// Avec C# 7.0
public void PrintCoordinates(Point p)
{
     p.GetCoordinates(out int x, out int y);
     WriteLine($"({x}, {y})");
}

Comme nous pouvons le voir, le type de variable suit directement le mot-clef out. Celui-ci peut être une classe, un type var, int, etc… Ceci permet donc de réduire un peu la longueur du code. Mais je vous accorde que dans cet exemple son utilisation peut paraître limitée … Voici donc, pour conclure cette nouvelle fonctionnalité dans un exemple plus ”parlant” et surtout plus intéressant.

public void PrintStars(string s)
{
     if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); }
     else { WriteLine("Cloudy - no stars tonight!"); }
}

Ici, dans cet exemple, on tente de convertir un type string en un entier naturel. Le TryParse retourne donc un booléen. Si l’opération se déroule avec succès alors une variable, ici i, va être créée et sera donc accessible dans le bloc suivant uniquement. Dans le cas contraire un message d’erreur fera son apparition.

De plus, si la fonction ou méthode que vous souhaitez appeler retourne deux paramètres out, il vous ait possible d’en refuser un avec le simple caractère ”_”. Voir l’exemple ci-dessous.

p.GetCoordinates(out var x, out _); // On ne récupère ici que le premier paramètre de sortie

Tuples

Cette nouvelle fonctionnalité permet de retourner plusieurs paramètres, en sortie d’une méthode ou d’une fonction. Et non pas simplement une seule comme traditionnellement avec un simple return. Tuple types et tuple literals font donc maintenant leurs apparitions !

//== Première façon de faire
// Déclaration de la méthode
(string, string, string) LookupName(long id) // tuple return type
{
 return (first, middle, last); // tuple literal
}

// Utilisation
var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}."); // par défaut Item1, Item2, etc....

//== Deuxième façon de faire
// Déclaration de la méthode
(string first, string middle, string last) LookupName(long id) // tuple return type
{
 return (first, middle, last); // tuple literal
}

// Utilisation
var names = LookupName(id);
WriteLine($"found {names.first} {names.last}."); // Avec le nom défini précédemment

//== Troisième façon de faire
// Déclaration de la méthode
(string, string, string) LookupName(long id) // tuple return type
{
 return (first: first, middle: middle, last: last); // On défini les noms ici
}

// Utilisation
var names = LookupName(id);
WriteLine($"found {names.first} {names.last}."); // Avec le nom défini précédemment

Il n’y a rien de plus parlant que du code. Voilà donc comment, à travers différentes manières, retourné plusieurs valeurs via un return. Cette fonctionnalité peut être, ma foi, très pratique dans certains cas.

Correspondance des modèles (Pattern matching)

Déjà fonctionnelle en Java depuis pas mal de temps déjà, cette fonctionnalité se faisait attendre en C#. Et bien la voilà ! Pour décrire au mieux cette fonctionnalité je vous renvoie à la description officielle de celle-ci :

C# 7.0 introduces the notion of patterns, which, abstractly speaking, are syntactic elements that can test that a value has a certain “shape”, and extract information from the value when it does.

Plus simplement, cela permet de connaître le type d’une variable et de la comparer simplement par l’intermédiaire de deux nouveaux mots-clefs:

// Utilisation du mot-clef 'is'
public void PrintStars(object o)
{
     if (o is null) return; // constant pattern "null"
     if (!(o is int i)) return; // type pattern "int i"
     WriteLine(new string('*', i));
}

if (o is int i || (o is string s && int.TryParse(s, out i)) { /* use i */ }

// Utilisation des modèles dans un switch
switch(shape)
{
     case Circle c:
         WriteLine($"circle with radius {c.Radius}");
         break;
     case Rectangle s when (s.Length == s.Height):
         WriteLine($"{s.Length} x {s.Height} square");
         break;
     case Rectangle r:
         WriteLine($"{r.Length} x {r.Height} rectangle");
         break;
     default:
         WriteLine("");
         break;
     case null:
         throw new ArgumentNullException(nameof(shape));
}

À vous de l’utiliser en fonction des cas. Mais combiné avec l’héritage cela peut être très pratique ainsi que  puissant ! Je précise que Microsoft a fait part de son envie d’approfondir cette fonctionnalité et l’utilisation des modèles / patterns dans le futur. Ne vous étonnez donc pas de voir son utilisation s’amplifier très prochainement.

Les fonctions locales

Je suis à titre personnel moins emballé par cette nouvelle fonctionnalité, mais elle a au moins le mérite d’exister. Les fonctions locales, dans une fonction, telle une variable, font donc maintenant leur apparition. Comme Microsoft le souligne, il arrive parfois que nous n’ayons besoin d’une fonction qu’à un seul endroit et nulle part ailleurs. Pour alléger la consommation de mémoire il sera donc maintenant possible de faire des fonctions qui n’existeront et qui ne pourrons être appelées QUE dans certaines fonctions. Bref, un code est toujours plus parlant !

public int Fibonacci(int x)
{
     if (x < 0) throw new ArgumentException("Less negativity please!", nameof(x));
     return Fib(x).current;

    int Fib(int i)
    {
         if (i == 0) return (1, 0);
         var (p, pp) = Fib(i - 1);
         return (p + pp);
     }
}

Bien entendu, l’utilisation de tuple est aussi possible dans des fonctions locales.

Throw

Un peu plus de flexibilité de Throw, celui-ci peut maintenant être utilisé plus facilement à des endroits où il provoquait des erreurs encore récemment avec une ancienne version du langage C#.

class Person
{
     public string Name { get; }
     public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));
     public string GetFirstName()
     {
         var parts = Name.Split(" ");
         return (parts.Length > 0) ? parts[0] : throw new InvalidOperationException("No name!");
     }
     public string GetLastName() => throw new NotImplementedException();
}

Voilà donc les grosses lignes de la version 7.0 du langage C#. Cependant, je le rappelle, cette liste n’est pas exhaustive et d’autres fonctionnalités mineures ont aussi fait leur apparition lors de cette mise à jour. Sans parler de l’optimisation du compilateur et des performances en général des applications utilisant ce langage.