Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Microsoft dévoile des fonctionnalités de C# 7.0 opérationnelles dans Visual Studio "15" Preview 4
Comme les tuples ou encore le filtrage par motif

Le , par Stéphane le calme

191PARTAGES

10  0 
Bien que C# 7.0 soit encore en développement, Microsoft a donné plus de détails sur des fonctionnalités qui, pour la plupart, sont déjà opérationnelles sur la préversion 4 de Visual Studio "15" qui a été proposée en téléchargement au courant de la semaine. La version 7.0 du langage orienté objet du framework .NET met un accent particulier sur la consommation de données, la simplification du code ainsi que la performance.

C # 7.0 introduit la notion de patterns (modèles) que Microsoft décrit comme étant des éléments syntaxiques qui peuvent vérifier qu'une valeur a un certain « motif » et en extraire des informations une fois que la vérification est faite. Le filtrage par motif (pattern matching) permet de vérifier si l'objet du filtrage possède une structure donnée, s'il s'y trouve telle ou telle sous-structure spécifique et/ou éventuellement pour substituer quelque chose d'autre aux motifs reconnus. Comme exemple de filtres dans C # 7.0, Microsoft évoque :
  • les filtres constants de la forme c (où c est une expression constante dans C #), qui servent à vérifier que l’entrée est égale à c ;
  • les filtres de type T x (où T est un type et x un identifiant), qui servent à vérifier qu’une entrée est bien du type T et, si c’est le cas, extraire la valeur de l’entrée dans une nouvelle variable x de type T ;
  • les filtres de la forme var x (où x est un identifiant), qui effectuent toujours des correspondances et mettent simplement la valeur de l'entrée dans une variable x du même type que l'entrée.

Voici un exemple avec un filtre constant et un filtre de type

Code C# : Sélectionner tout
1
2
3
4
5
6
public void PrintStars(object o) 
{ 
    if (o is null) return;     // filtre constant "null" 
    if (!(o is int i)) return; // filtre de type "int i" 
    WriteLine(new string('*', i)); 
}

Microsoft explique que les filtrages par motifs et les structures de contrôles vont souvent bien ensemble. Raison pour laquelle par exemple dans les instructions switch, qui permettent de tester plusieurs valeurs pour une expression, Microsoft a apporté des changements :
  • pour permettre d’effectuer un switch sur n’importe quel type (qui se limitait jusqu’à présent aux expressions de type scalaire comme les entiers, les caractères, les énumérations ou les booléens) ;
  • pour permettre d’utiliser des filtres dans les déclarations de série d’instructions (case) ;
  • pour permettre l’ajout de conditions additionnelles sur les clauses case.

Voici un simple exemple :

Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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("<unknown shape>"); 
        break; 
    case null: 
        throw new ArgumentNullException(nameof(shape)); 
}

Microsoft apporte toutefois quelques précisions suite à l’extension de la structure de contrôle switch. Désormais, l’ordre des clauses case importe désormais, comme dans les clauses catch, étant donné que ces clauses ne sont plus nécessairement disjointes. De plus, comme dans les clauses catch, le compilateur va vous aider à repérer des cas évidents qui ne seront jamais atteints. La clause par défaut est toujours évaluée en dernier : dans l’exemple ci-dessus, bien que la clause « null » vienne en dernier, elle sera évaluée avant la clause par défaut.

Les tuples, qui facilitent la possibilité de retourner plusieurs résultats, sont également annoncés comme étant l’un des changements les plus significatifs apportés avec cette version. Notons qu’avant la venue des tuples, il était possible de retourner plusieurs résultats. Cependant, les options n’étaient pas optimales. C# 7.0 proposent aux développeurs de se servir des littéraux et des types.

Code C# : Sélectionner tout
1
2
3
4
5
(string, string, string) LookupName(long id) // tuple return type 
{ 
    ... // va retirer first, middle et last from du stockage de données 
    return (first, middle, last); // littéral du tuple 
}

Cette méthode retourne trois chaînes de caractères encapsulées dans un tuple. Lors de l’appel de la méthode, la fonction pourra recevoir ce tuple et accéder à ses éléments individuels

Code C# : Sélectionner tout
1
2
var names = LookupName(id); 
WriteLine($"found {names.Item1} {names.Item3}.");

Item1 et etc. sont des noms par défaut des éléments d’un tuple et peuvent toujours être utilisés. Ils ne sont pas très descriptifs, il vous est possible de les modifier selon votre convenance. Par exemple

Code C# : Sélectionner tout
(string first, string middle, string last) LookupName(long id) // les éléments du tuple ont des noms

Cette fois-ci, lors de l’appel de la méthode, il ne sera plus question de faire appel à Item1 mais plutôt :

Code C# : Sélectionner tout
1
2
var names = LookupName(id); 
WriteLine($"found {names.first} {names.last}.");

Il est également possible de spécifier les noms directement dans les littéraux

Code C# : Sélectionner tout
return (first: first, middle: middle, last: last); // éléments nommés du tuple dans un littéral

Pour gérer les tuples, une syntaxe de déclaration de déclaration de déconstruction permet aux développeurs de séparer le tuple (ou d’autres ensembles de valeurs) en plusieurs parts et assigner ces parts à de nouvelles variables.

Code C# : Sélectionner tout
1
2
(string first, string middle, string last) = LookupName(id1); // déclaration de déconstruction 
WriteLine($"found {first} {last}.");

Dans une déclaration de déconstruction, il est possible d’utiliser var pour les variables individuelles déclarées :

Code C# : Sélectionner tout
(var first, var middle, var last) = LookupName(id1); // var à l'intérieur

Ou alors de choisir de mettre un seul var à l’extérieur des parenthèses

Code C# : Sélectionner tout
var (first, middle, last) = LookupName(id1); // var à l'extérieur

Il est également possible de déconstruire un tuple dans des variables existantes en se servant d’une affectation de déconstruction :

Code C# : Sélectionner tout
(first, middle, last) = LookupName(id2); // affectation de déconstruction

Les améliorations au niveau du littéral permettent de se servir de « _ » comme séparateur de chiffres dans un littéral. Microsoft estime que cela permet d’améliorer la lisibilité et reste sans effet sur la valeur.

Code C# : Sélectionner tout
1
2
var d = 123_456; 
var x = 0xAB_CD_EF;

En outre, C # 7.0 introduit la notion de littéraux binaires afin qu’il vous soit possible de spécifier les modèles de bit directement au lieu d’avoir à connaître la notation hexadécimale par cœur.

Code C# : Sélectionner tout
var b = 0b1010_1011_1100_1101_1110_1111;

Le nombre de types qui peuvent être retournés par des fonctions asynchrones a été étendu. Microsoft parle aussi des fonctions locales qui permettent aux développeurs de déclarer des fonctions auxiliaires dans le corps d’autres fonctions

Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
public int Fibonacci(int x) 
{ 
    if (x < 0) throw new ArgumentException("Less negativity please!", nameof(x)); 
    return Fib(x).current; 
  
    (int current, int previous) Fib(int i) 
    { 
        if (i == 0) return (1, 0); 
        var (p, pp) = Fib(i - 1); 
        return (p + pp, p); 
    } 
}

Source : blog Microsoft

Voir aussi :

Visual Studio "15" Preview 4 est disponible, avec un nouveau moteur d'installation qui fait passer la taille de l'installation minimum à 400 Mo

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de Jonyjack
Membre averti https://www.developpez.com
Le 29/08/2016 à 11:51
Code : Sélectionner tout
1
2
if (!(o is int i)) return; // filtre de type "int i"
WriteLine(new string('*', i));
Perso je trouve pas ça très clair de déclarer "i" et de lui affecter la valeur de "o" ainsi. Ce n'est pas si explicite à la lecture.
Je trouve ça bien plus clair en l'écrivant ainsi :
Code : Sélectionner tout
1
2
if (!(o is int)) return;
WriteLine(new string('*', (int)o));
Pour le reste, c'est plutôt bienvenu
1  0 
Avatar de esperanto
Membre expérimenté https://www.developpez.com
Le 01/09/2016 à 13:31
Citation Envoyé par DotNetMatt Voir le message
La nouvelle ecriture me parait plutot logique, ca evite de devoir faire un cast derriere, comme ce serait le cas avec la solution que tu proposes (car is est deja implemente donc changer son comportement pourrait causer des problemes de retrocompatibilite).
Le problème avec cette écriture, c'est qu'elle n'est pas cohérente avec la notion de bloc.

Imaginons que j'écrive:
Code : Sélectionner tout
1
2
if (!(o is int i)) { ... quelque chose sans return } // filtre de type "int i"
WriteLine(new string('*', i));
quelle est ici la portée de la variable i?

Ce qui serait plus logique ce serait d'avoir ceci:
Code : Sélectionner tout
1
2
if (o is int i) { WriteLine(new string('*', i));return; } // filtre de type "int i"
// et ici du code qui utilise o mais pas i
Ainsi l'idée serait que le bloc commence à if et se termine avec l'accolade.
1  0 
Avatar de SaiRictus
Membre du Club https://www.developpez.com
Le 29/08/2016 à 11:20
Les améliorations concernant l'utilisation des tuples sont bienvenues.
Il n'est pas rare d'avoir besoin de renvoyer un résultat "complexe" à partir d'une méthode et devoir créer un objet générique qui sera uniquement utiliser dans ce cas est fastidieux et sans grande valeur ajoutée. Ma réticence à utiliser les tuples actuellement était essentiellement due au fait que les noms des propriétés étaient très peu explicites et ça pouvait rendre la relecture du code extrêmement difficile dès lors qu'on utilisait des tuples à plus de 3 propriétés (et encore plus obscur si les propriétés en question étaient du même type).
1  1 
Avatar de rubisvert
Membre à l'essai https://www.developpez.com
Le 30/08/2016 à 11:37
Je ne sais pas si vous avez déjà la réponse, mais est-ce qu'il sera possible d'utiliser C#7 avec visual studio community prochainement?
Merci.
0  0 
Avatar de DotNetMatt
Modérateur https://www.developpez.com
Le 30/08/2016 à 19:01
Citation Envoyé par Jonyjack Voir le message
Code : Sélectionner tout
1
2
if (!(o is int i)) return; // filtre de type "int i"
WriteLine(new string('*', i));
Perso je trouve pas ça très clair de déclarer "i" et de lui affecter la valeur de "o" ainsi. Ce n'est pas si explicite à la lecture.
Je trouve ça bien plus clair en l'écrivant ainsi :
Code : Sélectionner tout
1
2
if (!(o is int)) return;
WriteLine(new string('*', (int)o));
La nouvelle ecriture me parait plutot logique, ca evite de devoir faire un cast derriere, comme ce serait le cas avec la solution que tu proposes (car is est deja implemente donc changer son comportement pourrait causer des problemes de retrocompatibilite).
0  0 
Avatar de shenron666
Expert confirmé https://www.developpez.com
Le 01/09/2016 à 14:49
Les filtres... dubitatif je suis

Le switch:
bienvenu mais attention à la clarté du code

Les tuples:
Intérêt limité, les tuples existent déjà
Si je veux nommer les variables autrement que par Item*, je retourne un type anonyme récupéré dans un dynamic
Par la suite, je refactorise si nécessaire

L'utilisation du underscore _ pour rendre plus lisible un litéral est une bonne idée, surtout pour les valeurs hex

Enfin les litéraux binaires, il était temps.
0  0 
Avatar de DotNetMatt
Modérateur https://www.developpez.com
Le 01/09/2016 à 18:45
Citation Envoyé par esperanto  Voir le message
Le problème avec cette écriture, c'est qu'elle n'est pas cohérente avec la notion de bloc.

Imaginons que j'écrive:
Code : Sélectionner tout
1
2
if (!(o is int i)) { ... quelque chose sans return } // filtre de type "int i" 
WriteLine(new string('*', i));
quelle est ici la portée de la variable i?

Ce qui serait plus logique ce serait d'avoir ceci:
Code : Sélectionner tout
1
2
if (o is int i) { WriteLine(new string('*', i));return; } // filtre de type "int i" 
// et ici du code qui utilise o mais pas i
Ainsi l'idée serait que le bloc commence à if et se termine avec l'accolade.

En effet, par contre sur le blog de Microsoft ils ecrivent :
As you can see, the pattern variables – the variables introduced by a pattern – are similar to the out variables described earlier, in that they can be declared in the middle of an expression, and can be used within the nearest surrounding scope. [...]

Qu'entendent-ils par "nearest surrounding scope" ? De ce que je comprend, comme pour une variable out, elle sera utilisable dans le bloc parent. Donc par exemple :
Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
public void PatternVariable() 
{ 
    if (!isDemo) 
    { 
        if (!(o is int i)) { ... quelque chose sans return } // filtre de type "int i" 
        WriteLine(new string('*', i)); 
  
        // Dans ce bloc on peut utiliser i 
    } 
  
    // Ici i n'existe pas 
}
Ce n'est qu'une supposition, je n'ai pas encore eu l'occasion de titiller la preview 4. Quelqu'un peut confirmer/infirmer ceci ?
0  0 
Avatar de ijk-ref
Membre confirmé https://www.developpez.com
Le 02/09/2016 à 2:44
Citation Envoyé par shenron666 Voir le message
Les tuples:
Intérêt limité, les tuples existent déjà
Si je veux nommer les variables autrement que par Item*, je retourne un type anonyme récupéré dans un dynamic
Par la suite, je refactorise si nécessaire
Mon dieu ! Utiliser du dynamic, c'est pour rire j'espère !? Vérifier le typage et les noms de variables à l'exécution quelle idée de génie Oo

Sinon je n'ai pas pu encore bien étudier le truc mais j'espère qu'ils ont effectué un rapprochement entre les types anonymes et ces nouveaux tuples. A première vue les types anonymes devraient maintenant ne plus être anonymes.
1  1 
Avatar de esperanto
Membre expérimenté https://www.developpez.com
Le 02/09/2016 à 9:55
Citation Envoyé par DotNetMatt  Voir le message
Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
public void PatternVariable() 
{ 
    if (!isDemo) 
    { 
        if (!(o is int i)) { ... quelque chose sans return } // filtre de type "int i" 
        WriteLine(new string('*', i)); 
  
        // Dans ce bloc on peut utiliser i 
    } 
  
    // Ici i n'existe pas 
}

Il y a quand même un souci : dans la partie { ... quelque chose sans return } on peut utiliser i alors même que ce bloc n'est exécuté que si o n'est pas du bon type... alors i vaut quoi?
0  0 
Avatar de François DORIN
Expert éminent sénior https://www.developpez.com
Le 02/09/2016 à 11:21
Citation Envoyé par Stéphane le calme  Voir le message
Voici un exemple avec un filtre constant et un filtre de type

Code C# : Sélectionner tout
1
2
3
4
5
6
public void PrintStars(object o) 
{ 
    if (o is null) return;     // filtre constant "null" 
    if (!(o is int i)) return; // filtre de type "int i" 
    WriteLine(new string('*', i)); 
}

Je ne suis pas convaincu par les filtres de type. D'accord, cela apporte quelque chose, puisqu'il est possible de vérifier le type et de réaliser le cast en une seule opération, mais je trouve que cela pénalise la lecture du code. J'aurais bien vu l'assignation dans une variable déjà déclarée en lieu et place à la déclaration d'une variable au sein même du is. Pour ma part, je pense que je privilégierais les extensions de switch au lieu d'utiliser le filtre de type, et écrire ainsi quelque chose du style :
Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
  
switch(o) 
{ 
  case int i: 
    WrilteLine(new string('*', i)); 
    break; 
  default: 
    break; 
}


Par contre, le filtre constant me plait, même si dans l'exemple, il ne donne rien puisqu'on aurait pu écrire if (o == null) return;

Et je n'ai pas compris à quoi servait le dernier type.

Citation Envoyé par Stéphane le calme  Voir le message
Microsoft explique que les filtrages par motifs et les structures de contrôles vont souvent bien ensemble. Raison pour laquelle par exemple dans les instructions switch, qui permettent de tester plusieurs valeurs pour une expression, Microsoft a apporté des changements :
  • pour permettre d’effectuer un switch sur n’importe quel type (qui se limitait jusqu’à présent aux expressions de type scalaire comme les entiers, les caractères, les énumérations ou les booléens) ;
  • pour permettre d’utiliser des filtres dans les déclarations de série d’instructions (case) ;
  • pour permettre l’ajout de conditions additionnelles sur les clauses case.

Voici un simple exemple :

Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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("<unknown shape>"); 
        break; 
    case null: 
        throw new ArgumentNullException(nameof(shape)); 
}

Une bonne nouvelle. Cela va permettre de remplacer des cascades de if par une structure bien plus lisible.

Citation Envoyé par Stéphane le calme  Voir le message
Les tuples, qui facilitent la possibilité de retourner plusieurs résultats, sont également annoncés comme étant l’un des changements les plus significatifs apportés avec cette version. Notons qu’avant la venue des tuples, il était possible de retourner plusieurs résultats. Cependant, les options n’étaient pas optimales. C# 7.0 proposent aux développeurs de se servir des littéraux et des types.

Code C# : Sélectionner tout
1
2
3
4
5
(string, string, string) LookupName(long id) // tuple return type 
{ 
    ... // va retirer first, middle et last from du stockage de données 
    return (first, middle, last); // littéral du tuple 
}

Une bonne chose et une mauvaise chose. Bien utilisée, cette notion va apporter un gain en clarté. Mal utilisée par contre, se sera la profusion de types anonymes...

Citation Envoyé par Stéphane le calme  Voir le message
Les améliorations au niveau du littéral permettent de se servir de « _ » comme séparateur de chiffres dans un littéral. Microsoft estime que cela permet d’améliorer la lisibilité et reste sans effet sur la valeur.

Code C# : Sélectionner tout
1
2
var d = 123_456; 
var x = 0xAB_CD_EF;

En outre, C # 7.0 introduit la notion de littéraux binaires afin qu’il vous soit possible de spécifier les modèles de bit directement au lieu d’avoir à connaître la notation hexadécimale par cœur.
Code C# : Sélectionner tout
var b = 0b1010_1011_1100_1101_1110_1111;

Un gain indéniable pour la lisibilité. Je ne peux qu'approuver. Et la notation binaire était attendue depuis longtemps ! Cela va permettre de gagner aussi en clarté lors de la déclaration d'énumération de type drapeaux (Flags).

Citation Envoyé par Stéphane le calme  Voir le message
Le nombre de types qui peuvent être retournés par des fonctions asynchrones a été étendu. Microsoft parle aussi des fonctions locales qui permettent aux développeurs de déclarer des fonctions auxiliaires dans le corps d’autres fonctions

Code C# : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
public int Fibonacci(int x) 
{ 
    if (x < 0) throw new ArgumentException("Less negativity please!", nameof(x)); 
    return Fib(x).current; 
  
    (int current, int previous) Fib(int i) 
    { 
        if (i == 0) return (1, 0); 
        var (p, pp) = Fib(i - 1); 
        return (p + pp, p); 
    } 
}

Pour l'instant, je suis mitigé. Je vois des avantages (ça évite de définir une fonction qui ne sera utilisée que par une seule, et couplé avec les tuples, le gain est indéniable), mais aussi des inconvénients (cela alourdi le code de la fonction "mère"). Quoiqu'il en soit, l'exemple donné me laisse pantois, dans la mesure où la fonction Fib est utilisée avant d'être défini ! La lecture séquentielle du code en prend un sacré coup...
0  0