Bonjour,Cette discussion est destinée à recevoir vos commentaires concernant l'article Les nouveautés de C# 7.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.
Bonjour,Une erreur dans cette actualité ? Signalez-nous-la !
1 |
0 |
) , les entreprises suivent t-elles le rythme ?
0 |
0 |

0 |
0 |
les entreprises suivent t-elles le rythme ?
Ça dépend lesquelles... Dans ma boite on a adopté VS2015 et C# 6 très vite après leur sortie, du moins dans mon équipe. En fait, je pense que jusqu'ici, l'adoption des nouvelles versions du langage a été freinée par la nécessité d'acheter une nouvelle version de Visual Studio à chaque fois ; mais j'ai cru comprendre qu'il était prévu que ça change. C# évolue de plus en plus vite, et je ne pense pas que MS pourra continuer à sortir une nouvelle version de VS pour chaque nouvelle version du langage.
Il est déjà possible, via ce package NuGet, d'utiliser la dernière version du compilateur dans une version plus ancienne de Visual Studio. Par contre, vu que l'IDE lui-même ne supporte pas les nouvelles features, il y aura plein d'erreurs affichées au niveau de l'éditeur, même si ça compile... ce qui réduit un peu l'intérêt.
les fonctions locales je reste sceptique
Disons que ça évite les fonctions "helper" qui traînent un peu n'importe où dans le code, qui au fil des modifications se retrouvent loin de la méthode qui les utilise, etc. Ça permet d'avoir un code mieux organisé. Et puis les fonctions locales ont l'avantage, par rapport à des fonctions normales, de pouvoir accéder aux variables et paramètres de la méthode où elles sont déclarées.
Pour moi un des cas où c'est utile est la validation des arguments pour un itérateur ou une méthode asynchrone. En effet, un itérateur est "lazy", c'est à dire que le corps de la méthode ne commence à s'exécuter que quand on commence à énumérer le résultat, et non lors de l'appel de la méthode. Si bien que ce code ne donne pas le résultat voulu :
void Test()
{
var enumerable = Repeat("test", -1); // Pas d'erreur
using (var enumerator = enumerable.GetEnumerator()) // Toujours pas d'erreur
{
enumerator.MoveNext(); // Erreur ici
}
}
IEnumerable
{
if (count < 0)
throw new ArgumentOutOfRangeException("count must be positive");
for (int i = 0; i < count; i++)
{
yield return item;
}
}
Pour que les arguments soient validés immédiatement lors de l'appel, il faut écrire la méthode Repeat comme ceci :
IEnumerable
{
if (count < 0)
throw new ArgumentOutOfRangeException("count must be positive");
return RepeatIterator(item, count);
}
IEnumerable
{
for (int i = 0; i < count; i++)
{
yield return item;
}
}
Ce qui n'est pas super pratique... Avec les fonctions locales, on peut faire ça :
IEnumerable
{
if (count < 0)
throw new ArgumentOutOfRangeException("count must be positive");
IEnumerable
{
for (int i = 0; i < count; i++)
{
yield return item;
}
}
return Iterator();
}
0 |
0 |
Je suis très étonné par le choix de "Deconstruct"public void Deconstruct(out double x, out double y)
{
x = this.X;
y = this.Y;
}
Je comprends qu'en interne ils veuillent se reposer sur du C# "ancien" mais pour l'utilisateur final d'aujourd'hui le but des nouveaux "tuples" n'est-il pas justement d'éviter au maximum l'utilisation peu pratique et archaïque de "out" ?
L'utiliser sous cette forme me semblerait plus judicieuse :public (double x, double y) Deconstruct() => (this.X, this.Y);
Ensuite la possibilité de déclarer directement une variable "out" est une bonne chose... mais pour moi elle repose toujours sur le mauvais choix de continuer à utiliser "out"
AMHA une autre direction comme avec un mécanisme d'"exception light" - quand la gestion d'exception classique n'est pas obligatoire ou trop lourde.
En introduisant par exemple deux nouveaux mots clés, un permettant une sortie incorrecte (sans lancer d'exception) d'une méthode et un autre pour la tester :
if (try int i = int.ParseLight(s))
{
Console.WriteLine($"La chaine représente un entier de valeur {i}");
}
Et comme je réfléchis en même temps que j'écris... je pense même qu'il serait possible de transformer les Parses avec exceptions classiques pour qu'ils fonctionnent... classiquement et aussi dans un mode light se suffisant d'un simple test booléen comme le code au-dessus.
Sinon avec C# 7 on doit pouvoir écrire cela (bon c'est un peu plus verbeux mais adieux les "out" :lol:):
if (!((bool error, int i) = int.ParseLight2(s)).error)
{
Console.WriteLine($"La chaine représente un entier de valeur {i}");
}
Autrement dans ton switch Pattern matching ça fonctionnerait un "goto Circle c when c.Radius < 5" ? 8O
Sinon n'y aurait-il pas une petite faute dans ton exemple sur la généralisation du type de retour des méthodes asynchrones ? AMHA la variable "price" en attente n'est pas déclarée puisqu'avant elle est interne au if.
0 |
0 |
Je suis très étonné par le choix de "Deconstruct"public void Deconstruct(out double x, out double y)
{
x = this.X;
y = this.Y;
}
Je comprends qu'en interne ils veuillent se reposer sur du C# "ancien" mais pour l'utilisateur final d'aujourd'hui le but des nouveaux "tuples" n'est-il pas justement d'éviter au maximum l'utilisation peu pratique et archaïque de "out" ?
C'est juste au niveau de la déclaration ; au niveau de l'utilisation, tu n'as pas besoin de out. Et de toute façon c'est juste si tu veux ajouter à une classe la possibilité de la déconstruire, pour les tuples tu n'as rien à faire, c'est déjà fait.
L'utiliser sous cette forme me semblerait plus judicieuse :public (double x, double y) Deconstruct() => (this.X, this.Y);
C'était ma première réaction, mais en fait il y a une bonne raison : avec la syntaxe choisie, tu peux avoir plusieurs méthodes Deconstruct, avec des signatures différentes :
public void Deconstruct(out double x, out double y)
{
x = this.X;
y = this.Y;
}
public void Deconstruct(out double x, out double y, out double z)
{
x = this.X;
y = this.Y;
z = this.Z;
}
Avec ce que tu proposes, ce ne serait pas possible, vu que tu ne peux pas avoir deux méthodes qui ne diffèrent que par le type de retour.
AMHA une autre direction comme avec un mécanisme d'"exception light" - quand la gestion d'exception classique n'est pas obligatoire ou trop lourde.
En introduisant par exemple deux nouveaux mots clés, un permettant une sortie incorrecte (sans lancer d'exception) d'une méthode et un autre pour la tester :
if (try int i = int.ParseLight(s))
{
Console.WriteLine($"La chaine représente un entier de valeur {i}");
}
J'aime bien le principe... tu peux le proposer sur le GitHub Roslyn ;)
Et comme je réfléchis en même temps que j'écris... je pense même qu'il serait possible de transformer les Parses avec exceptions classiques pour qu'ils fonctionnent... classiquement et aussi dans un mode light se suffisant d'un simple test booléen comme le code au-dessus.
Le problème des méthodes Parse qui renvoient des exceptions, c'est que les exceptions ne sont pas seulement lourdes en terme de syntaxe, mais aussi en terme de performance. Lancer et catcher une exception, ça coûte cher...
Sinon avec C# 7 on doit pouvoir écrire cela (bon c'est un peu plus verbeux mais adieux les "out" :lol:):
if (!((bool error, int i) = int.ParseLight2(s)).error)
{
Console.WriteLine($"La chaine représente un entier de valeur {i}");
}
Oula, pas sûr que ce soit plus lisible... Ce qui serait plus intéressant, c'est une méthode TryParse qui renvoie un Nullable
if (int.TryParse(s) is int i)
{
...
}
Autrement dans ton switch Pattern matching ça fonctionnerait un "goto Circle c when c.Radius < 5" ? 8O
Non, ça marche pas. Tu peux toujours faire un goto vers un case
Sinon n'y aurait-il pas une petite faute dans ton exemple sur la généralisation du type de retour des méthodes asynchrones ? AMHA la variable "price" en attente n'est pas déclarée puisqu'avant elle est interne au if.
Non non, c'est correct. Pendant la phase de design il y a eu pas mal de discussions sur le scope d'une variable déclarée comme ça, mais la rendre interne au if posait trop de problèmes. Exemple type de pourquoi c'est problématique :
if (!int.TryParse(s, out int i))
{
// i est défini, mais ça valeur n'a pas de sens puisque TryParse a échoué
}
else
{
// TryParse a réussi, mais i n'est pas défini
}
Donc au final le scope de la variable est le scope parent du if.
0 |
0 |
C'était ma première réaction, mais en fait il y a une bonne raison : avec la syntaxe choisie, tu peux avoir plusieurs méthodes Deconstruct, avec des signatures différentesAh oui merci !
Le problème des méthodes Parse qui renvoient des exceptions, c'est que les exceptions ne sont pas seulement lourdes en terme de syntaxe, mais aussi en terme de performance. Lancer et catcher une exception, ça coûte cher...Mais c'est exactement ce que je sous entendais : lourdes dans tous les sens ;)
C'est pourquoi j'imagine qu'en interne les méthodes traitées avec "exceptions lights" auraient un code optimisé et différent. Leurs exceptions retirées et remplacées pour une sortie de fonction classique... une seconde sortie comme je l'explique ici.
J'ai un peu modifié mon "try" après tes précisions sur le scope des variables "out" pour le rendre plus général par exemple si l'on souhaite sortir un code d'erreur ou n'importe quoi - qui n'a absolument pas besoin d'être de type "Exception".
try int i = int.Parse(s) otherwise i = 0;
try double j = double.Parse(s) otherwise return;
try int k = int.Parse(s) otherwise Console.WriteLine("impossible à parser"); // Erreur à la compilation car 'k' n'est pas toujours défini (pour la suite du code)
try DX.DrawTriangles(vertices) otherwise(int error) { Console.WriteLine("Erreur dans le code n°" + error); return; }
J'aime bien le principe... tu peux le proposer sur le GitHub Roslyn ;)J'aimerai bien. C'est dommage de rencontrer ici si peu de personnes le maîtrisant (dont moi). Et je ne trouves vraiment pas beaucoup d'exemples varié sur le sujet.
Non, ça marche pas. Tu peux toujours faire un goto vers un case
Ils auraient mieux fait de l'implémenter loin de ce "switch" sans les ":" et "break".
0 |
0 |
0 |
0 |

0 |
0 |