Voir le flux RSS

François DORIN

[Actualité] Retour sur une proposition pour C# 8 : les références nullables

Noter ce billet
par , 25/03/2018 à 19h43 (5204 Affichages)
L'erreur à un milliard de dollars
Connaissez-vous Tony Hoare ? Il y a de grandes chances que non. Pourtant, en tant que développeurs, vous utilisez très certainement le fruit de ses travaux. L'un d'entre eux, qu'il qualifie lui-même comme "l'erreur à 1 milliard de dollars", est le pointeur nul, objet du billet d'aujourd'hui.

Très pratique, le pointeur nul est présent dans de nombreux langages. C++, Java, C# pour ne prendre que les exemples de langages les plus répandus.

Par exemple, en C#, la valeur nulle est la valeur par défaut des types références. Si vous allouez un tableau d'objets, chaque case du tableau est initialisée… à nulle ! Ou encore, lorsqu'on souhaite préciser qu'une variable n'a pas de valeur, on l'initialise là aussi à nulle.

Mais qui n'a jamais eu à souffrir, durant l'exécution de son code, d'un NullPointerException ? Que le développeur à qui cela n'est jamais arrivé lève la main ! Cette erreur est fréquente (sans doute l'erreur la plus répandue !) et indique l'absence de valeur là où on en attend une. Cela oblige donc à tester la valeur de la variable avant de l'utiliser.

Et c'est là que réside le problème de la plupart des langages ayant cette notion. Il n'y a rien pour distinguer une valeur nulle fortuite d'une valeur nulle voulue.

Évolution du langage C#
Le lien avec C#8 ? Il y a actuellement de longues discussions autour d'une fonctionnalité portant le nom de "référence nullable"(nullable reference dans la langue de Shakespeare), qui permettrait justement de préciser si une référence peut être nulle ou non.

Si on passe outre l'aspect rupture du langage, quels seraient les avantages d'une telle approche ? Et bien le principal avantage est de permettre de corriger de nombreux bogues à la compilation, et non plus à l'exécution.

Ainsi, un code comme
Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
if (hello.Length == 0)
{}

Générera une erreur si hello est une variable de type String?, mais pas d'erreur si elle est de type String.

Pour un type String?, il faudra vérifier que la référence est non nulle
Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
if (hello != null && hello.Length == 0)
{}

En théorie, c'est très intéressant, mais cela soulève encore de nombreux problèmes, dont toutes les questions n'ont pas encore trouvé réponse. Par exemple, que se passe-t-il si une méthode teste le caractère nul d'une variable ? Par exemple :
Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
String? Hello = "world";
if (!String.IsNullOrEmpty(hello))
{
   int taille = hello.Length; // Générera une erreur !
}
Pourquoi une erreur ? Nous, en tant que développeurs, nous savons que la méthode String.IsNullOrEmpty teste le caractère nul ou vide d'une chaîne de caractères. Mais le compilateur ne le sait pas lui. Pour résoudre ce problème, deux approches sont envisageables :
  1. La première, rajouter un test de nullité dans la conditionnelle. Pas terrible. En plus d'être redondant, cela alourdit le code ;
  2. La seconde, introduire une nouvelle syntaxe, permettant de dire au compilateur "OK, je sais ce que je fais, ne génère pas d'erreur" (par exemple, int taille = hello.Length!;).


Des zones d'ombre...
De plus, si cette approche permettrait de résoudre certains problèmes, elle n'est pas sans en poser d'autres, qu'il va aussi falloir résoudre avant de pouvoir déployer cette fonctionnalité :
  • une valeur (notamment une propriété) peut très bien devenir nulle entre le moment où elle a été testée et où la valeur est effectivement utilisée ;
  • des méthodes qui retournent des valeurs non nulles pourront malgré tout retourner des valeurs nulles (cas d'une API qui n'aurait pas été mise à jour par exemple).


Une évolution ou une révolution ?
Le plus gros problème de cette approche reste qu'elle implique une évolution même du langage, évolution "cassante" changeant la sémantique de tout code écrit jusqu'à aujourd'hui. On comprend donc les longues discussions qu'il peut y avoir à ce sujet.

Illustrons ceci avec quelques exemples. Aujourd'hui, ce code est tout à fait valide :
Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
String hello = "World";
Hello = null;

Demain, ce code ne sera plus valide, et générera une erreur au niveau de la seconde ligne, lors de l'affectation de la valeur null.

Autrement dit, par défaut, les références ne seront plus nullables. Pour retrouver le comportement actuel, il faudra déclarer les variables comme étant nullables :
Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
String? hello = "World";
Hello = null; // Opération valide, puisque hello est nullable.

Pour éviter de casser tout le code existant, ce changement ne générera que des avertissements, et non des erreurs. Mais un code propre d'aujourd'hui pourra se retrouver avec des milliers d'avertissements demain !

Affaire à suivre
Les discussions sont loin d'être terminées. On parle déjà de C#8 alors que la prochaine version attendue de C# est la 7.3. Mais ce changement est loin d'être anodin et il est donc nécessaire de l'anticiper largement en amont, car s'il venait à être adopté, cela sera un changement majeur du langage comme jamais il n'en a connu jusqu'à aujourd'hui.

Sources
le projet C# sur github
exemple de discussion sur Reddit (213 commentaires au moment de l'écriture de ce billet, ce qui est énorme !)

Envoyer le billet « Retour sur une proposition pour C# 8 : les références nullables » dans le blog Viadeo Envoyer le billet « Retour sur une proposition pour C# 8 : les références nullables » dans le blog Twitter Envoyer le billet « Retour sur une proposition pour C# 8 : les références nullables » dans le blog Google Envoyer le billet « Retour sur une proposition pour C# 8 : les références nullables » dans le blog Facebook Envoyer le billet « Retour sur une proposition pour C# 8 : les références nullables » dans le blog Digg Envoyer le billet « Retour sur une proposition pour C# 8 : les références nullables » dans le blog Delicious Envoyer le billet « Retour sur une proposition pour C# 8 : les références nullables » dans le blog MySpace Envoyer le billet « Retour sur une proposition pour C# 8 : les références nullables » dans le blog Yahoo

Mis à jour 18/04/2018 à 14h39 par François DORIN

Catégories
DotNET , C#

Commentaires

Page 1 sur 2 12 DernièreDernière
  1. Avatar de Madmac
    • |
    • permalink
    Mais qui n'a jamais eu à souffrir, durant l'exécution de son code, d'un NullPointerException ? Que le développeur à qui cela n'est jamais arrivé lève la main ! Cette erreur est fréquente (sans doute l'erreur la plus répandue !) et indique l'absence de valeur là où on en attend une. Cela oblige donc à tester la valeur de la variable avant de l'utiliser.
    Seul les programmeurs qui n'ont jamais eu a vivre avec l'alternative s'en plaignent. Les premières version de pascal n'assignait pas de valeur automatiquement. Avec résultat que les pointeurs pointaient des adresses d'une façon aléatoire. Et dans ce cas de figure, il n'y avait aucun test qui permettait de déterminer si la valeur pointée en était véritablement une. Donc forcément, les programmeurs devaient s'inventer un type NULL d'une façon non conventionnelle. Essayer de faire des fonctions pour des listes chaînées sans null, vous allez vous amuser.

    Pour un type String?, il faudra vérifier que la référence est non nulle
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if (hello != null && hello.Length == 0)
    {
       …
    }
    C'est un problème uniquement si le programmeur est trop paresseux pour assigne la valeur "" à ses chaînes de caractère au moment de la construction. L'avantage de déclarer dynamiquement ses variable est que le programmeur ne peut utiliser classe Nul à tort et à travers. Le problème vient plutôt de la nature du Null. Si Null est une classe, il toujours identifiable en tant qu'objet. Et vous ne pouvez pas l'assigner à une variable d'une autre classe, à moins que cette classe ait la classe Null comme ancêtre.
    Mis à jour 27/03/2018 à 01h53 par Madmac
  2. Avatar de codec_abc
    • |
    • permalink
    Citation Envoyé par Madmac
    Seul les programmeurs qui n'ont jamais eu a vivre avec l'alternative s'en plaignent. [,..]
    Faux (et la suite du message aussi). Le problème de null/nil dans les langages c'est que c'est un trou dans le système de type. Quand une fonction retourne une string on s'attend a un avoir une string valide et pas un objet manquant. Du coup, la vrai solution est d'utiliser les "sum types" ou les génériques pour modéliser l'absence possible de valeur. Haskell/OCaml/F#/Rust/Swift (entre autres) ont depuis toujours géré les valeurs manquantes de cette façon et ont pu bannir les erreurs de ce type depuis le début.

    PS: Une liste chainée en Haskell en 1 ligne de code sans possibilité d'accéder à des éléments qui n'existe pas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    data MyList a = Cons a (MyList a) | MyNil
    Mis à jour 29/03/2018 à 10h00 par codec_abc
  3. Avatar de François DORIN
    • |
    • permalink
    @Madmac

    Le problème n'est pas tant d'avoir une valeur nulle, mais la gestion de cette valeur dans de nombreux langages, où le nul fait parti intégrante des valeurs possibles par défaut. En C# ou en Java par exemple, si une méthode retourne une instance de MaClasse, on ne peut pas savoir si cette valeur sera une véritable instance ou si elle sera nulle. Ainsi, si on appelle une méthode instance.IsEmpty() par exemple, on ne saura pas, sans tester caractère nul, si cette méthode s'exécutera normalement ou si elle générera une exception.

    A contrario, si on créé une instance statique de la classe (votre proposition), par exemple MaClasse.Null, alors on peut l'utiliser en tant que valeur nulle. Cela permet d'appeler instance.IsEmpty() sur cette valeur et cela ne générera pas d'erreur (si on admet que cette méthode renvoie true pour une valeur nulle). Pour les opérations "invalides" (par exemple, de modification pour une instance représentant la valeur nulle), on peut générer une exception (par exemple, InvalidArgumentException en C#).

    Mais ce n'est pas forcément idéal, car elle oblige à traiter une instance précise de manière particulière dans le code de la classe MaClasse.

    Comme le dit @codec_abc, c'est un trou dans le typage, que cette évolution de C# vise à combler, en s'inspirant notamment de ce qui se fait dans les langages fonctionnels. Mais le C# étant par nature un langage procédural, cela ne va pas sans poser de problèmes, comme expliqué dans le billet.
  4. Avatar de jpouly
    • |
    • permalink
    Je ne comprend pas ce problème de Null Reference Exception. j'ai beau le tourner dans tous les sens, je ne comprend pas pourquoi c'est une nécessite.

    Les tests, unitaires ou fonctionnelles, devraient éviter ce genre d'erreurs, non ? Et l'informatique, c'est déterministe non ?

    Et le choix d'un exemple avec une chaine de caractère je ne le comprend pas non plus. Et le caractère null n'existe pas en .NET, c'est pas du C .
    Et la classe String est un peu particulière, puisque c'est un type immuable (Lecture seul). Je crois que c'est la seule dans le Framework.

    De plus, une des dernière nouveauté de Visual Studio 2015, c'est l'opérateur .? On peut écrire le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if ( hello?.Length ?? 0 == 0)
    Ou utiliser la méthode String.IsNullOrEmpty qui fonctionne très bien avec : String hello = null; (sans le ?).

    Enfin, pour répondre :
    Citation Envoyé par François DORIN
    @Madmac
    Le problème n'est pas tant d'avoir une valeur nulle, mais la gestion de cette valeur dans de nombreux langages, où le nul fait parti intégrante des valeurs possibles par défaut. En C# ou en Java par exemple, si une méthode retourne une instance de MaClasse, on ne peut pas savoir si cette valeur sera une véritable instance ou si elle sera nulle. Ainsi, si on appelle une méthode instance.IsEmpty() par exemple, on ne saura pas, sans tester caractère nul, si cette méthode s'exécutera normalement ou si elle générera une exception.
    C'est pour cela que les nouveaux opérateurs .? et ? sont utiles

    Citation Envoyé par François DORIN
    A contrario, si on créé une instance statique de la classe (votre proposition), par exemple MaClasse.Null, alors on peut l'utiliser en tant que valeur nulle. Cela permet d'appeler instance.IsEmpty() sur cette valeur et cela ne générera pas d'erreur (si on admet que cette méthode renvoie true pour une valeur nulle). Pour les opérations "invalides" (par exemple, de modification pour une instance représentant la valeur nulle), on peut générer une exception (par exemple, InvalidArgumentException en C#).
    Mais ce n'est pas forcément idéal, car elle oblige à traiter une instance précise de manière particulière dans le code de la classe MaClasse.
    Je ne comprend toujours pas le problème. La valeur nulle est un cas possible, qu'il faut tester. Sinon il y a une levée d'exception. C'est la philosophie des langages .NET et de Java.
    De plus, les instances statiques, ça s'appelle des singletons. Et ça va à l'encontre, l'a encore, de la philosophie du .NET et du ramasse miette.

    Citation Envoyé par François DORIN
    Comme le dit @codec_abc, c'est un trou dans le typage, que cette évolution de C# vise à combler, en s'inspirant notamment de ce qui se fait dans les langages fonctionnels. Mais le C# étant par nature un langage procédural, cela ne va pas sans poser de problèmes, comme expliqué dans le billet.
    C'est bien ce que je crains. A force de vouloir faire de la nouveauté, on fini par faire n'importe quoi.
  5. Avatar de Aurelien.Regat-Barrel
    • |
    • permalink
    Hello,

    Citation Envoyé par François DORIN
    A contrario, si on créé une instance statique de la classe (votre proposition), par exemple MaClasse.Null, alors on peut l'utiliser en tant que valeur nulle. Cela permet d'appeler instance.IsEmpty() sur cette valeur et cela ne générera pas d'erreur (si on admet que cette méthode renvoie true pour une valeur nulle). Pour les opérations "invalides" (par exemple, de modification pour une instance représentant la valeur nulle), on peut générer une exception (par exemple, InvalidArgumentException en C#).
    Je comprends pas l'intérêt : pour moi on a juste remplacé une syntaxe par une autre, à savoir if (x != null) / NullPointerException par if (!x.IsEmpty()) / InvalidArgumentException.

    Je suis perplexe aussi sur l'histoire du trou dans le système de type qui relève selon moi plutôt d'une spécificité à Java / C# dans la mesure où il n'y a pas distinction syntaxique entre le pointeur et l'objet pointé (déréférencement explicite). Mais avec un langage qui distingue clairement le pointeur de sa valeur pointée, null ne vient pas polluer les valeurs possibles pour le type pointé.

    C'est inhérent à l'utilisation abusive de new et par conséquent au peu de place accordé à la sémantique de valeur. Car quand il est possible de renvoyer une instance de classe par valeur comme on le fait avec un int, alors le problème disparaît car le pointeur a disparu - et donc null aussi.

    Et alors le type pointeur peut même être utilisé pour exprimer le caractère optionnel d'une variable, selon une sémantique très proche d'un type optional / maybe. Typiquement en C/C++, pour exprimer le caractère optionnel d'un argument d'input: void f(int p1, int * p2 = NULL);

    Mais c'est clair que pour les types de retour, une abstraction à la optional / maybe est beaucoup plus sympa et robuste!
  6. Avatar de François DORIN
    • |
    • permalink
    Citation Envoyé par jpouly
    Je ne comprend pas ce problème de Null Reference Exception. j'ai beau le tourner dans tous les sens, je ne comprend pas pourquoi c'est une nécessite.

    Les tests, unitaires ou fonctionnelles, devraient éviter ce genre d'erreurs, non ? Et l'informatique, c'est déterministe non ?
    L'objectif est d'avoir le maximum d'erreur dès la compilation. Les tests, c'est bien, mais :
    • il faut qu'ils soient écrits ;
    • il faut qu'ils soient complets du point de vue de la couverture de code ;
    • il faut qu'ils soient complets du point de vue des jeux de valeurs utilisés ;
    • il faut qu'ils soient maintenu en cas de mise à jour ;
    • il faut qu'ils soient juste.


    Citation Envoyé par jpouly
    Et le choix d'un exemple avec une chaine de caractère je ne le comprend pas non plus. Et le caractère null n'existe pas en .NET, c'est pas du C .
    J'aurais pu prendre une autre classe que la classe String, cela n'aurait pas changé grand chose.

    Au sujet du caractère nul, c'est une incompréhension. Mais j'admet que ma tournure de phrase peut semer le trouble Quand je dis "Par exemple, que se passe-t-il si une méthode teste le caractère nul d'une variable ?", le caractère nul n'est pas à prendre comme le caractère 0, mais la valeur de la variable. J'aurais du dire "que se passe-t-il si une méthode teste la nullité d'une variable ?". C'est d'ailleurs ce que j'avais écrit initialement, mais je trouvais ça moche Mais peut être moins "confusant".

    Citation Envoyé par jpouly
    De plus, une des dernière nouveauté de Visual Studio 2015, c'est l'opérateur .? On peut écrire le code suivant :

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    if ( hello?.Length ?? 0 == 0)
    Bobo les yeux Il faut rajouter au minimum un jeu de parenthèses.
    Tout d'abord, ça n'a rien à voir avec Visual Studio, mais avec C#. L'opérateur .? a été introduit avec C#6.

    Ensuite, si la variable hello n'est pas censée être nulle, dans votre code, vous en traduisez pourtant la possibilité.

    Citation Envoyé par jpouly
    Je ne comprend toujours pas le problème. La valeur nulle est un cas possible, qu'il faut tester. Sinon il y a une levée d'exception. C'est la philosophie des langages .NET et de Java.
    Comme dis plus haut, le problème c'est détection à l'exécution vs détection à la compilation.

    Citation Envoyé par jpouly
    De plus, les instances statiques, ça s'appelle des singletons. Et ça va à l'encontre, l'a encore, de la philosophie du .NET et du ramasse miette.
    Les instances statiques n'ont rien à voir avec le singleton dans le cas présent. D'ailleurs, en cas de singleton, il est inutile d'avoir une instance pour représenter la valeur nulle
    Je vous invite sérieusement à revoir la définition d'un singleton de manière approfondie. Un singleton est une classe dont il existe une seule et unique instance. Avoir une instance représentant la valeur nulle n'a donc aucun sens ici !

    En ce qui concerne le fait que cela va à l'encontre de la philosophie de .NET et du ramasse-miette, j'attends plus d'information. A commencer par une définition officielle (donc émanant de Microsoft) de cette philosophie. A défaut, votre affirmation est sans aucun fondement. (et ne cherchez pas, c'est le cas).


    Citation Envoyé par Aurelien.Regat-Barrel
    Je comprends pas l'intérêt : pour moi on a juste remplacé une syntaxe par une autre, à savoir if (x != null) / NullPointerException par if (!x.IsEmpty()) / InvalidArgumentException.
    D'une par, c'est une question de sémantique. Avec un NullPointerException, vous n'avez aucune information, si ce n'est qu'il y a un bug. Avec un InvalidArgumentException, vous savez déjà qu'il y a une opération non valide due à un argument invalide. Cela oriente déjà là recherche de bug.

    D'autre part, car la valeur nulle est une valeur particulière. Mais que dire s'il y a d'autres valeurs qui sont invalides ? Pourquoi ne pas générer la même exception ? Si vous faite un mécanisme d'authentification par exemple, vous devez avoir un mot de passe. Si le mot de passe est null, ou vide, vous pouvez déclencher la même exception, en précisant l'erreur porte sur le mot de passe. Si on laisse le NullPointerException, vous faite une distinction qui n'a pas lieu d'être.

    Citation Envoyé par Aurelien.Regat-Barrel
    Je suis perplexe aussi sur l'histoire du trou dans le système de type qui relève selon moi plutôt d'une spécificité à Java / C# dans la mesure où il n'y a pas distinction syntaxique entre le pointeur et l'objet pointé (déréférencement explicite). Mais avec un langage qui distingue clairement le pointeur de sa valeur pointée, null ne vient pas polluer les valeurs possibles pour le type pointé.
    Bingo ! C'est justement l'erreur à un milliard de dollars

    L'erreur consiste à gérer la valeur nulle de manière implicite. De nombreux problèmes seraient résolus ou évités en ayant une gestion explicite.
  7. Avatar de codec_abc
    • |
    • permalink
    Citation Envoyé par jpouly
    C'est bien ce que je crains. A force de vouloir faire de la nouveauté, on fini par faire n'importe quoi.
    C'est vachement prétentieux de sortir un truc pareil vu les personnes qui bossent sur l'évolution du langage d'autant plus qu'il apparait clairement que tu ne comprends pas vraiment le problème ni les enjeux. Ceci dit cela ne me choque pas vraiment car bossant quasiment uniquement en C# la moitié de mes collègues mélangent les notions de typage statique fort/faible statique/dynamique et l'inférence de type. C'est un peu triste j'ai l'impression que c'est aspect est complétement ignoré lors des formations diplômantes.
  8. Avatar de jpouly
    • |
    • permalink
    Citation Envoyé par François DORIN
    Les tests, c'est bien, mais :
    • il faut qu'ils soient écrits ;
    • il faut qu'ils soient complets du point de vue de la couverture de code ;
    • il faut qu'ils soient complets du point de vue des jeux de valeurs utilisés ;
    • il faut qu'ils soient maintenu en cas de mise à jour ;
    • il faut qu'ils soient juste.
    C'est bien le problème des tests, surtout quand ils sont automatisés. Mais cela n’empêche pas de les faire .

    Citation Envoyé par François DORIN
    Bobo les yeux Il faut rajouter au minimum un jeu de parenthèses.
    Tout d'abord, ça n'a rien à voir avec Visual Studio, mais avec C#. L'opérateur .? a été introduit avec C#6.
    C'est effectivement une évolution du C#6.

    Citation Envoyé par François DORIN
    Ensuite, si la variable hello n'est pas censée être nulle, dans votre code, vous en traduisez pourtant la possibilité.
    C'est bien ça le truc, il ne faut jamais considérer qu'une chose est acquise. ça évite les bugs

    Citation Envoyé par François DORIN
    Les instances statiques n'ont rien à voir avec le singleton dans le cas présent. D'ailleurs, en cas de singleton, il est inutile d'avoir une instance pour représenter la valeur nulle
    Je vous invite sérieusement à revoir la définition d'un singleton de manière approfondie. Un singleton est une classe dont il existe une seule et unique instance. Avoir une instance représentant la valeur nulle n'a donc aucun sens ici !
    En .net, un membre statique d'une classe est accessible par toutes les instances de cette classe. Voir par tout le monde si il est publique. Si c'est pas un singleton, faut admettre qu'il y a un cousinage

    Citation Envoyé par François DORIN
    En ce qui concerne le fait que cela va à l'encontre de la philosophie de .NET et du ramasse-miette, j'attends plus d'information. A commencer par une définition officielle (donc émanant de Microsoft) de cette philosophie. A défaut, votre affirmation est sans aucun fondement. (et ne cherchez pas, c'est le cas).
    Il y a bien la présentation du C# (https://docs.microsoft.com/fr-fr/dot...our-of-csharp/), mais je sens que ça ne va pas vous aller.
  9. Avatar de François DORIN
    • |
    • permalink
    Citation Envoyé par jpouly
    En .net, un membre statique d'une classe est accessible par toutes les instances de cette classe. Voir par tout le monde si il est publique. Si c'est pas un singleton, faut admettre qu'il y a un cousinage
    Pas du tout. Ce sont bien deux choses différentes. Ce n'est pas parce que l'un (singleton) utilise l'autre (attribut statique de classe) qu'ils sont cousins. Ils ont deux finalités radicalement différentes.


    Citation Envoyé par jpouly
    Il y a bien la présentation du C# (https://docs.microsoft.com/fr-fr/dot...our-of-csharp/), mais je sens que ça ne va pas vous aller.
    Effectivement, cela ne me convient pas. C'est pas que je sois borné (bon, si ça m'arrive), mais cela est bien insuffisant pour plusieurs raisons :
    • dans votre message initial, vous parlez de la philosophie .NET. Là, vous ne me sortez qu'un lien sur le C#. C'est bien méconnaître l'environnement .NET que de le réduire au C#. Il y a par exemple F#, qui lui justement n'a pas tout ces soucis avec la valeur nulle ;
    • c'est une présentation succincte du langage. Cela ne représente en rien la philosophie derrière ce langage ;
    • où est passée la "philosophie" du ramasse-miette ?


    En vous lisant, j'ai constaté que vous n'aviez qu'une connaissance superficielle et une compréhension erronée de ce qu'est le C#, la plateforme .NET ou encore le ramasse-miettes. C'est pas un reproche, on ne peut pas tout savoir, et avant de savoir, on est soi-même ignorant. Je vous invite à lire CLR via C#, c'est un excellent ouvrage, très complet, dans lequel vous apprendrez énormément de chose sur tout cela.
  10. Avatar de wadison
    • |
    • permalink
    Pour une liste vide, 99% des développeurs instancient une liste, et ne laisse pas l'objet à null!!
    Avec cette exemple très commun il est assez simple de comprendre l’intérêt de annihiler le null pour toutes les variables de son code! Mais il y a un blocage pour beaucoup, mais je ne comprend pas l’entêtement vue qu'il le font déjà pour des listes. "C'est ADN du langage limite un changement de paradigme"... Je pense que c'est très exagéré comme réaction.

    Que le langage l'impose est une très bonne chose, car actuellement nous sommes obligé de rester parano et tester null partout dans le code même si on décide de ne pas affecter de null dans son code.

    Deux clans se forment assez vite, c'est un sujet hyper passionné mais je suis content de ne plus être du coté obscur de la force , je me sens moins seul.

    l'idée n'est pas de remplacer null avec une variable representant "null" mais plutot instancier avec un objet le plus possible neutre.

    exemple 1 :
    En lieu et place (dans sa version courte ):
    monobject?.monchamp ?? "pas de valeur" autant faire un objet neutre avec monobject.monchamp = "pas de valeur"
    exemple 2 : pour les calculs mathématique, il faut utiliser le 1 pour les multiplication pour une addition le 0, etc...

    => dans de très rare cas avoir l'information que l'objet manipulé est "le" neutre est utile.

    Rien à voir...
    Une feature du langage C# que j’attends c'est la protection des méthodes mutable. Autrement dit pouvoir passer une variable en "lecture seule" qui interdit les appels de méthodes "mutable". Ceci permet une forte encapsulation sans faire de "clone()" dans les getter ou encore mettre en place des Interfaces "readonly".
    Ca existe sous C++ nativement.
    Mis à jour 29/03/2018 à 18h51 par wadison
  11. Avatar de jpouly
    • |
    • permalink
    Citation Envoyé par codec_abc
    C'est vachement prétentieux de sortir un truc pareil vu les personnes qui bossent sur l'évolution du langage d'autant plus qu'il apparaît clairement que tu ne comprends pas vraiment le problème ni les enjeux.
    Peut être que je suis prétentieux, mais j'ai l'impression que les dernières évolutions du langage n'apportent pas grand choses (à quelques exceptions prêt).

    D'où mon interrogation sur le fait de le faire évoluer coûte que coûte.

    Citation Envoyé par codec_abc
    Ceci dit cela ne me choque pas vraiment car bossant quasiment uniquement en C# la moitié de mes collègues mélangent les notions de typage statique fort/faible statique/dynamique et l'inférence de type.C'est un peu triste j'ai l'impression que c'est aspect est complètement ignoré lors des formations diplômantes.
    Je n'ai pas fait que du C# dans ma longue vie de développeur, j'ai même codé avec des langages un peu exotiques (le SCIL par exemple, du pur dynamique, sans aucun contrôle ).
    Ayant eu des cours de langages et compilations dans ma jeunesse (ça date, mais bon), je t'assure que je connais bien les différences et je les ai même expérimenté.
  12. Avatar de François DORIN
    • |
    • permalink
    Citation Envoyé par wadison
    Une feature du langage C# que j’attends c'est la protection des méthodes mutable. Autrement dit pouvoir passer une variable en "lecture seule" qui interdit les appels de méthodes "mutable". Ceci permet une forte encapsulation sans faire de "clone()" dans les getter ou encore mettre en place des Interfaces "readonly".
    Ca existe sous C++ nativement.
    Je viens de regarder rapidement sur le github du langage, et je n'ai rien vu de tel. C'est peut être une proposition à faire ?

    Maintenant, je me méfie de C++ qui permet presque de faire cela. Je dis presque car un attribut marqué comme mutable peut être modifié par une méthode déclarée const En jouant avec les pointeurs et les cast, on peut arriver à faire la même chose.

    En C#, on peut accéder et modifier n'importe quel attribut ou propriété via réflexion, rendant caduc ce genre d'approche. Donc je doute que cela soit accepté :/
  13. Avatar de jpouly
    • |
    • permalink
    Citation Envoyé par François DORIN
    Pas du tout. Ce sont bien deux choses différentes. Ce n'est pas parce que l'un (singleton) utilise l'autre (attribut statique de classe) qu'ils sont cousins. Ils ont deux finalités radicalement différentes.
    Expliquer moi comment on fait un singleton en C# alors.

    Citation Envoyé par François DORIN
    Effectivement, cela ne me convient pas. C'est pas que je sois borné (bon, si ça m'arrive), mais cela est bien insuffisant pour plusieurs raisons :
    • dans votre message initial, vous parlez de la philosophie .NET. Là, vous ne me sortez qu'un lien sur le C#. C'est bien méconnaître l'environnement .NET que de le réduire au C#. Il y a par exemple F#, qui lui justement n'a pas tout ces soucis avec la valeur nulle ;
    • c'est une présentation succincte du langage. Cela ne représente en rien la philosophie derrière ce langage ;
    • où est passée la "philosophie" du ramasse-miette ?
    Le sujet du billet est bien le C# non ?

    Citation Envoyé par François DORIN
    En vous lisant, j'ai constaté que vous n'aviez qu'une connaissance superficielle et une compréhension erronée de ce qu'est le C#, la plateforme .NET ou encore le ramasse-miettes. C'est pas un reproche, on ne peut pas tout savoir, et avant de savoir, on est soi-même ignorant. Je vous invite à lire CLR via C#, c'est un excellent ouvrage, très complet, dans lequel vous apprendrez énormément de chose sur tout cela.
    J'ai commencé à faire du .net avec la version 1.0 (Visual Studio .net en 2001) . Et j'ai vu évolué ce langage jusqu'à aujourd'hui.
    Je connais suffisamment la plateforme .NET, le VB.NET et le C# et j'ai fait suffisamment de projets dans des contextes totalement différents (système, web, winform, wpf, SqlServer, Oracle, ...)
    pour avoir un avis sur cette proposition d'évolution dans le langage.
  14. Avatar de François DORIN
    • |
    • permalink
    Citation Envoyé par jpouly
    Expliquer moi comment on fait un singleton en C# alors.
    Non, car là n'est pas la question. De plus, vous savez pertinemment comment on implémente un singleton.

    Citation Envoyé par jpouly
    Le sujet du billet est bien le C# non ?
    Oui. Mais c'est vous qui parlez de la philosophie de la plateforme .NET dans sa généralité, ainsi que celle du ramasse-miettes. J'attends donc des éclairages sur cela.
    Et comme je l'ai déjà précisé, le lien que vous avez donné n'est qu'une présentation rapide de C#, ce qui est bien loin d'une description de la philosophie du langage.

    Citation Envoyé par jpouly
    J'ai commencé à faire du .net avec la version 1.0 (Visual Studio .net en 2001) . Et j'ai vu évolué ce langage jusqu'à aujourd'hui.
    Je connais suffisamment la plateforme .NET, le VB.NET et le C# et j'ai fait suffisamment de projets dans des contextes totalement différents (système, web, winform, wpf, SqlServer, Oracle, ...)
    pour avoir un avis sur cette proposition d'évolution dans le langage.
    Malheureusement, et sans vouloir vous offenser, votre discours contraste avec votre expérience. Il en ressort que vous donnez votre avis sur un sujet que vous êtes loin de maitriser.
  15. Avatar de jpouly
    • |
    • permalink
    En fouillant sur le web, je suis tombé sur cet article : https://blogs.msdn.microsoft.com/dot...pes-in-csharp/

    Ma maitrise de la langue de Shakespeare n'est pas parfaite, mais je comprend mieux avec les exemples fournis.
    Si cela reste de l'ordre de la vérification syntaxique, c'est effectivement intéressant.

    Ce qui me rassure c'est ces deux phrases :

    4. There is no semantic impact of the nullability annotations, other than the warnings. They don’t affect overload resolution or runtime behavior, and generate the same IL output code. They only affect type inference insofar as it passes them through and keeps track of them in order for the right warnings to occur on the other end.
    5. There is no guaranteed null safety, even if you react to and eliminate all the warnings. There are many holes in the analysis by necessity, and also some by choice.

    Je crois que le choix de la classe String est source de confusion, car comme je l'ai dis précédemment, c'est une classe très particulière en .NET (en tout cas en VB et C#). Avec l'exemple d'une classe et de ses attributs, c'est beaucoup plus claire.

    Après, comme il s'agit de warnings et que dans bien des cas, l'analyse fonctionnera pas bien, cette fonctionnalité ne nous donne aucune garantie sur d'éventuelles NullReferenceException.
    Il sera donc toujours nécessaire de le vérifier.
    Mis à jour 30/03/2018 à 13h25 par jpouly
  16. Avatar de ijk-ref
    • |
    • permalink
    Proposition très intéressante ! Tout ce qui permet au compilateur de mieux nous comprendre (et donc mieux nous prévenir) est bon à prendre... surtout si ça se fait avec une syntaxe plus uniforme (type référence ou valeur avec "?" aurait la même signification)

    D'ailleurs si vous avez un article sur l'origine du choix de l'implémentation des types valeurs nullables (encapsulation dans un structure), je suis preneur Je m'attendais plus naturellement qu'ils deviennent des class et s'utilisent comme tels. (Ils vont peut être y venir pour uniformiser avec la proposition de référence nullable... ou pas)

    Ensuite j'aimerais beaucoup qu'ils passent à mon gros chantier des méthodes à multiples sorties (ne pas confondre avec à multiples valeurs) ce qui rendrait caduc les valeurs de sortie (out), optimiserait/simplifierait l'usage des exceptions, éviterait les codes redondant, permettrait de coder ses propres instructions (comme la dernière version de switch avec une syntaxe un peu différente évidemment) sans devoir passer par de la compilation (même si c'est plus facile aujourd'hui avec Roslyn). Je sais personne n'y croit mais moi si
  17. Avatar de François DORIN
    • |
    • permalink
    Citation Envoyé par ijk-ref
    D'ailleurs si vous avez un article sur l'origine du choix de l'implémentation des types valeurs nullables (encapsulation dans un structure), je suis preneur Je m'attendais plus naturellement qu'ils deviennent des class et s'utilisent comme tels. (Ils vont peut être y venir pour uniformiser avec la proposition de référence nullable... ou pas)
    Je n'ai pas cela sous la main non. De mémoire, les types nullables datent de C#2. A cette époque, l'évolution du langage n'était pas ouverte comme elle l'est aujourd'hui.

    Citation Envoyé par ijk-ref
    Ensuite j'aimerais beaucoup qu'ils passent à mon gros chantier des méthodes à multiples sorties (ne pas confondre avec à multiples valeurs) ce qui rendrait caduc les valeurs de sortie (out), optimiserait/simplifierait l'usage des exceptions, éviterait les codes redondant, permettrait de coder ses propres instructions (comme la dernière version de switch avec une syntaxe un peu différente évidemment) sans devoir passer par de la compilation (même si c'est plus facile aujourd'hui avec Roslyn). Je sais personne n'y croit mais moi si
    On peut avoir plus d'information ? Car là, ce n'est pas très parlant
  18. Avatar de ijk-ref
    • |
    • permalink
    Citation Envoyé par François DORIN
    On peut avoir plus d'information ? Car là, ce n'est pas très parlant
    J'en ai déjà parlé. Je vais retenter de mieux revendre ma proposition

    But : En respectant la philosophie procédurale, réaliser ce que tous bons langages fonctionnelles savent faire naturellement.

    Aujourd'hui encore nous sommes obligés d'écrire 3 méthodes ayant pourtant des corps très semblables.
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int Parse(string s);
    int Parse(string s, int default_value);
    bool TryParse(string s, out int i);
    Alors qu'une seule et unique pourrait les remplacer :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    int MyParse(string s) | void MyErreur();
    La signature de cette méthode veut dire qu'elle a une sortie retournant un entier et une autre sortie retournant rien du tout.
    En gros, elle normaliserait simplement les sorties des exceptions... par des sorties "standards".
    Dans le corps de la méthode il y aurait juste à remplacer les "throw new Exception();" par des "MyError.return;"
    Comment utiliser une telle méthode ? J'avance cette syntaxe :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    MyParse("3.14")->(int r)
    {
      Console.Writeline("resultat : " + r);
    }
    |MyError()->()
    {
      Console("n'est pas un entier");
    }

    Prenez le temps d'y réfléchir svp . C'est conceptuellement bien plus riche et précis pour le compilateur que le fameux TryParseOut.
    Car là au font ça réalise vraiment ce que l'on demande : une sortie avec le résultat et une autre sortie sans rien si pas de résultat.
    Contrairement à la bidouille du TryParseOut qui retourne principalement! un résultat secondaire! (le booléen) qu'il faudra inutilement traiter en plus.

    Ensuite cette proposition permettrait de traiter simplement des cas bien plus complexes. Tel qu'un "switch" ou le traitement d'événements :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Event0(mouseMove)->(var r)
    {
      label.Content = "location : " + r.GetPosition(this);
    }
    |Event1(mouseDown)->(MouseButtonArgs r)
    {
      label.Content = "down button : " + r.MouseButton;                
    }
    |Event2(mouseUp)->(MouseButtonArgs r)
    {
      label.Content = "up button : " + r.MouseButton;                
    }

    Ce dernier exemple est ma principalement motivation pour cette enrichissement car le langage actuel ne permet pas de le faire efficacement.

    Note : non je ne la proposerais pas moi-même sur Github car ce n'est déjà pas facile de répondre aux remarques en français... alors dans une autre langue...
  19. Avatar de François DORIN
    • |
    • permalink
    Citation Envoyé par ijk-ref
    J'en ai déjà parlé. Je vais retenter de mieux revendre ma proposition
    Ne pas hésitez à donner le lien vers la discussion correspondante

    Dans la suite, je me fais l'avocat du diable (volontairement). N'y voyez absolument rien de personnel, mais si vous devez défendre cette idée, il faut bien la prendre dans tous les sens

    Citation Envoyé par ijk-ref
    Aujourd'hui encore nous sommes obligés d'écrire 3 méthodes ayant pourtant des corps très semblables.
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int Parse(string s);
    int Parse(string s, int default_value);
    bool TryParse(string s, out int i);
    En voici une seule, qui remplie bien le rôle :
    [CODE=C#]int? Parse(string s);[/QUOTE]


    Citation Envoyé par ijk-ref
    Alors qu'une seule et unique pourrait les remplacer :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    int MyParse(string s) | void MyErreur();
    La signature de cette méthode veut dire qu'elle a une sortie retournant un entier et une autre sortie retournant rien du tout.
    En gros, elle normaliserait simplement les sorties des exceptions... par des sorties "standards".
    C'est difficilement lisible (donc peu de chance d'être accepté en l'état. On a du mal à voir ce que signifie MyError. Pourquoi pas une syntaxe du genre :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    int | void MyParse(string s)
    Beaucoup plus lisible, on voit bien que le type de retour peut être soit int, soit void.


    Citation Envoyé par ijk-ref
    Dans le corps de la méthode il y aurait juste à remplacer les "throw new Exception();" par des "MyError.return;"
    Il faudrait un exemple complet pour bien comprendre. Un sans la syntaxe, et un avec la proposition.

    Citation Envoyé par ijk-ref
    Comment utiliser une telle méthode ? J'avance cette syntaxe :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    MyParse("3.14")->(int r)
    {
      Console.Writeline("resultat : " + r);
    }
    |MyError()->()
    {
      Console("n'est pas un entier");
    }
    Syntaxe difficile à lire. On a l'impression que MyError est un appel de fonction, alors qu'en réalité ce n'est pas le cas.

    Il est possible, depuis C#7, de réaliser quelque chose de très similaire à cela grâce au pattern matching
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    switch (shape)
    {
        case Rectangle r:
            Console.WriteLine($"Rectangle with area {r.Width * r.Height}");
            break;
        case Circle c when c.Radius < 5:
            Console.WriteLine($"Small circle");
            break;
        case Circle c:
            Console.WriteLine($"Large circle");
            break;
    }

    Pourquoi ne pas l'utiliser ?


    Citation Envoyé par ijk-ref
    Ensuite cette proposition permettrait de traiter simplement des cas bien plus complexes. Tel qu'un "switch" ou le traitement d'événements :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Event0(mouseMove)->(var r)
    {
      label.Content = "location : " + r.GetPosition(this);
    }
    |Event1(mouseDown)->(MouseButtonArgs r)
    {
      label.Content = "down button : " + r.MouseButton;                
    }
    |Event2(mouseUp)->(MouseButtonArgs r)
    {
      label.Content = "up button : " + r.MouseButton;                
    }

    Ce dernier exemple est ma principalement motivation pour cette enrichissement car le langage actuel ne permet pas de le faire efficacement.
    Je n'ai rien compris à cet exemple. Que représentent mouseMove, mouseDown et mouseUp ? Comment sont-ils initialisés ? Donnez un code d'exemple en C# valide puis avec la nouvelle syntaxe, qu'on puisse bien comprendre l'apport.
    De plus, comment peut-on avoir le mot clé "var" dans un endroit pareil ? Il est impossible d'utiliser l'inférence de type ici pour déterminer le type de la variable

    Citation Envoyé par ijk-ref
    Note : non je ne la proposerais pas moi-même sur Github car ce n'est déjà pas facile de répondre aux remarques en français... alors dans une autre langue...
    Si ce n'est pas facile de répondre aux remarques, c'est sans doute que l'idée n'est pas assez travaillée C'est aussi en cela que c'est difficile de gérer les ajouts, il ne faut pas juste considérer un cas d'usage en particulier, mais bien tous les impacts que cela peut avoir. Ce qui fait que l'exercice est loin d'être facile.
  20. Avatar de ijk-ref
    • |
    • permalink
    Citation Envoyé par François DORIN
    Si ce n'est pas facile de répondre aux remarques, c'est sans doute que l'idée n'est pas assez travaillée
    Surement un peu Sauf que tu sais aussi (avec ton article) qu'il sera très difficile de faire passer une idée comme travaillée devant quelqu'un ne comprenant déjà ce qu'elle apporte ou résout.

    Le premier gros travail est d'être sûr qu'on parle bien de la même chose et que nos attentes d'un langage ne soient pas contradictoires. Par exemple il est quasi vain de parler d'améliorations de détection d'erreur à la compilation avec une personne ne jurant que par "dynamic". C'est un peu le souci de penser que je devrais utiliser du "Pattern matching"

    Citation Envoyé par François DORIN
    C'est aussi en cela que c'est difficile de gérer les ajouts, il ne faut pas juste considérer un cas d'usage en particulier
    Je ne connais pas d'autre moyen que d'utiliser des exemples particuliers pour expliquer un ajout qui sera tout sauf pour un usage "unique".

    Ne te focalise pas trop sur ses difficultés (réelles) d'intégration (pas plus qu'une proposition Github) sinon tu ne verras jamais la "Lumière"

    Citation Envoyé par François DORIN
    De plus, comment peut-on avoir le mot clé "var" dans un endroit pareil ? Il est impossible d'utiliser l'inférence de type ici pour déterminer le type de la variable
    Voila, clairement je me suis pas du tout fait comprendre. Pourtant quand j'ai écrit :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    MyIntParse(str)->(var i) { Console.Writeline(i); }
    Tu avais l'air de comprendre que "i" est la sortie de la méthode. Donc l'inférence de type est parfaitement déterminé. Cette syntaxe permettrait de dire au compilateur de rentrer dans le block de code qui suit uniquement en cas de sortie normal de la méthode. Si la méthode arrive à une exception passer à la suite SANS lancer/initialiser l'exception.

    Ne vois-tu pas l'intérêt pour nous et le compilateur de permettre une tel optimisation ? Sa syntaxe ne te plait peut-être pas car tu n'y vois pas une flèche "->" pour un retour de valeur. T'y vois plus le "=>" déclaratif et le remplaçant du "(*pointer)." en C++. Il y a p'être mieux. Si t'en suggères une plus approprié n'hésite pas. A défaut du TryParseOut qui n'est qu'une bidoule demandant au compilateur de faire plus qu'il ne devrait, Surement que tu préfèrerais :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    try(var i = MyIntParse(str)) { Console.Writeline(i); }
    Sauf que ma syntaxe est faite pour être bien plus générale et traiter d'autres cas.

    Comme par exemple construire une méthode permettant de s'abonner à une liste d'évènements, d'attendre et de s'y désabonner au 1er évènement déclenché en sortant de la méthode avec les arguments de l'évènement dans block de code approprié. Non, on ne peut pas le faire actuellement avec vérification statique et une syntaxe lisible/concise !

    Si une tel traitement était spécifiquement codé en dur (dans le compilateur) avec sa propre syntaxe, une boucle traitant des messages/évènements pourrait ressembler à cela :
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    while(true)
    {
      await message(MouseEventArgs r of MouseMove)
      {
        label.Content = "mouse location : " + r.GetPosition(this); 
      }
      or message(MouseButtonEventArgs r of MouseDown)
      {
        label.Content = "mouse button down : " + r.ChangedButton;
      }
      or message(MouseButtonEventArgs r of MouseUp)
      {
        label.Content = "mouse button up : " + r.ChangedButton;
      }
      or message(KeyEventArgs m of KeyEventArgs}
      {
        if(m.Key==Key.Escape) break;
      }
    }
    Ceci permettait dans le code courant d'une "Window" d'afficher les infos de la souris et de quitter cette affichage en appuyant sur Echappe (en imaginant que cette syntaxe existe !)
    J'espère que tu vois bien que ceci permettrait d'écrire proprement des boucles de messages imbriquées comme une action spécifique au click droit de la souris pour déplacer un objet. Du code suivable à la ligne !

    Evidemment cette syntaxe ne pourrait pas être retenue car elle ouvre la porte à tellement d'autre chose... d'où celle utilisée dans mon dernier message... que tu n'as pas compris

    J'arrête là pour aujourd'hui - en espérant que je me suis mieux fait comprendre.
Page 1 sur 2 12 DernièreDernière