Developpez.com - Rubrique .NET

Le Club des Développeurs et IT Pro

Découvrir les futures fonctionnalités qui viendront avec C# 7.3

Qu'en pensez-vous ?

Le 03/06/2018, par François DORIN, Expert éminent sénior
Un petit tour sur le GitHub du langage C# nous permet d'avoir un aperçu des nouvelles fonctionnalités pour la prochaine version du langage C# : la version 7.3.


Au jour d'aujourd'hui, ces fonctionnalités sont au nombre de 9. Les voici :
  • attributs sur le champ d'une propriété implémentée automatiquement ;
  • contrainte type non managé ;
  • variable out ;
  • amélioration de la résolution en cas de surcharge ;
  • réassignement des alias ;
  • autoriser l'initialisation de tableaux alloués sur la pile ;
  • opérateurs d'égalités == et != pour les tuples ;
  • améliorations autour du mot-clé fixed.


Attributs sur le champ d'une propriété implémentée automatiquement
Aujourd'hui, il n'est pas possible d'appliquer un attribut sur le champ d'une propriété implémentée automatiquement. Il est donc nécessaire d'écrire entièrement la propriété :
Code C# :
1
2
3
4
5
6
7
8
9
10
[Serializable] 
public class Foo { 
    [NonSerialized] 
    private string MySecret_backingField; 
  
    public string MySecret { 
        get { return MySecret_backingField; } 
        set { MySecret_backingField = value; } 
    } 
}

Une des propositions de C# 7.3 serait la possibilité d'écrire le code suivant :
Code C# :
1
2
3
4
5
[Serializable] 
public class Foo { 
    [field: NonSerialized] 
    public string MySecret { get; set; } 
}
Ainsi, l'attribut sera appliqué un champ d'une propriété implémentée automatiquement et non sur la propriété elle-même. Cette approche est déjà utilisable pour les événements. Il s'agit donc uniquement d'une extension du langage.

Néanmoins il y a quelques soucis qui pourrait freiner son adoption. Aujourd'hui, cette syntaxe, bien que compilable, génère un avertissement et l'attribut est simplement ignoré. Autoriser cette écriture impliquerait donc un changement de comportement par rapport aux versions précédentes, qui pourrait avoir des conséquences en cas de recompilation de code, puisque l'attribut serait cette fois-ci pris en compte.

Contrainte type non managé
La contrainte "type non managé" permettrait de rendre générique du code qui aujourd'hui ne peut l'être. Cette fonctionnalité est à destination de développeurs de bibliothèques de bas niveau, notamment dans un cadre d'interopérabilité. Par exemple, elle permettrait de définir une méthode ainsi :
Code C# :
1
2
3
4
void Hash<T>(T value) where T : unmanaged 
{ 
    ... 
}

Aujourd'hui, ce n'est pas possible. Les développeurs sont donc obligé de générer autant de surcharges pour la méthode qu'il y a de types à supporter :
Code C# :
1
2
int Hash(Point point) { ... }  
int Hash(TimeSpan timeSpan) { ... }

Mais même si cela pourrait avoir des avantages sur la maintenance du code et sur les performances, cela pourrait avoir des répercutions sur l'ensemble de l'éco-système .NET. A voir donc si cette fonctionnalité sera maintenue dans la prochaine version du langage.

Variables out
C# 7 a introduit la possibilité de déclarer et d'utiliser une variable out en même temps, c'est-à-dire d'autoriser ce type de code :
Code C# :
1
2
3
4
if (int.TryParse(input, out int result)) 
    WriteLine(result); 
else 
    WriteLine("L'entrée n'est pas un entier");

Faute de temps pour mesurer correctement l'impact de ce type d'expression pour l'initialisation des attributs ou dans les constructeurs, cette possibilité n'avait pas été rendue possible. Ce sera maintenant chose possible avec C# 7.3, qui autorisera donc ce genre de code :
Code C# :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System.Collections.Generic; 
using System.Linq; 
  
public class A 
{ 
    public A(int i) 
    { 
    } 
  
    public static int Magic = int.TryParse("123", out var i) ? i : 0; 
} 
  
public class B : A 
{ 
    public B(string s) 
        : base(int.TryParse(s, out var i) ? i : 0) 
    { 
    } 
}

Amélioration en cas de surcharge
Les règles permettant de sélectionner la bonne méthode en cas de surcharge ont été affinées. Tout en gardant la rétrocompatibilité (sans quoi la recompilation de certains codes pourrait modifier leur comportement !), de nouvelles règles ont été ajoutées afin de résoudre automatiquement certains cas qui généraient une erreur jusqu'à présent.

Notamment, en cas de présence de membres statique et d'instance, seuls les membres pertinents sont sélectionnés. Ainsi, les surcharges statiques seront ignorées lors de l'appel d'une méthode via une instance et inversement.

De même, dans le cadre de la généricité, si les contraintes applicables au type sont incompatibles, la méthode sera ignorée (par exemple, une méthode générique acceptant un paramètre de type valeur ne sera pas retenue lors de la résolution si le paramètre fourni est de type référence).

Réassignement des alias
C# 7.2 a introduit la possibilité de créer des alias d'une variable. Par exemple, dans le code suivant :
Code C# :
ref int x = ref y;
les variables x et y sont tous simplement des alias.

Mais il n'était pas possible de réassigner un alias pour qu'il devienne un alias d'une autre variable. Ce sera a priori le cas avec C# 7.3, qui autorisera un réassignement :
Code C# :
1
2
ref int x = ref y; 
x = ref z; // redéfinition de l'alias. x est dorénavant équivalent à z, et non plus à y.

Autoriser l'initialisation de tableaux déclarés sur la pile
Il est possible de déclarer des tableaux sur la pile. Malheureusement, il n'est pas possible aujourd'hui de les initialiser. Cela sera possible avec la prochaine version de C#, qui lève cette restriction :
Code C# :
1
2
3
4
5
6
7
  
// actuellement valide 
stackalloc int[3];  
  
 // les déclarations suivantes seront valides avec la prochaine version de C# 
stackalloc int[3] { 1, 2, 3 }; 
stackalloc int[] { 1, 2, 3 };

Opérateurs d'égalité == et != pour les tuples
Une des propositions pour cette évolution du langage sera le support des opérateurs d'égalité (et donc d'inégalité) pour les tuples.

Ainsi, il sera possible d'écrire t1 == t2 qui serait alors traduit en un code sémantiquement équivalent (par exemple t1.Item1 == t2.Item1 && t1.Item2 == t2.Item2).

Amélioration de la prise en charge du mot clé fixed
Il y a également des nouveautés concernant l'utilisation du mot clé fixed.

D'une part, il ne sera plus nécessaire d'épingler une variable lors de l'accès à un de ses membres épinglé lorsque cet accès se fait via un index. Ainsi, dans le code suivant :
Code C# :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsafe struct S 
{ 
    public fixed int myFixedField[10]; 
} 
  
class Program 
{ 
    static S s; 
  
    unsafe static void Main() 
    { 
        int p = s.myFixedField[5]; // indexing fixed-size array fields would be ok 
    } 
}
Il ne sera pas nécessaire d'épingler la variable s avant de l'utiliser pour accéder au 6e élément de son attribut myFixedFiled.

Dans un autre ordre d'idée, mais toujours autour du mot clé fixed, il est envisagé un mécanisme permettant de préciser qu'un type utilisateur peut être épinglé et récupérer un pointeur natif et donc autoriser des codes comme :
Code C# :
1
2
3
4
5
fixed(byte* ptr = byteArray) 
{ 
   // ptr est un pointeur natif sur le premier élément du tableau. 
   // byteArray est protégé et ne peut être déplacé ou collecté par le ramasse-miettes pour toute la durée de ce bloc. 
}

Actuellement, les types autorisés dans cette construction sont codés en dur. L'idée serait donc d'assouplir cela et de permettre à un type nouvellement défini d'apparaitre dans ce type de construction. Cela servirait notamment pour des structures introduites récemment comme ImmutableArray<T> ou Span<T>.

Source : compte GitHub du projet C#

Et vous ?

Qu'en pensez-vous  ?
Quelles fonctionnalités souhaiteriez-vous voir en sus ?
  Billet blog