Pourquoi les paramètres const ne sont-ils pas autorisés en C #?

102

Cela semble étrange, en particulier pour les développeurs C ++. En C ++, nous avions l'habitude de marquer un paramètre comme constafin d'être sûr que son état ne sera pas modifié dans la méthode. Il y a aussi d'autres raisons spécifiques au C ++, comme passer const refpour passer par ref et être sûr que l'état ne sera pas changé. Mais pourquoi ne pouvons-nous pas marquer comme paramètres de méthode const en C #?

Pourquoi ne puis-je pas déclarer ma méthode comme suit?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....
Incognito
la source
C'était toujours juste une fonctionnalité de sécurité en C ++, et jamais vraiment intégrée au langage tel que je le voyais. Les développeurs C # ne pensaient évidemment pas que cela ajoutait beaucoup de valeur.
Noldorin
3
Une plus grande discussion à cette question: "Const-correctness in c #": stackoverflow.com/questions/114149/const-correctness-in-c
tenpn
Il existe pour les types valeur [out / ref], mais pas pour les types référence. C'est une question intéressante.
kenny
4
Comment cette question pourrait-elle avoir à la fois les balises C # et indépendantes du langage?
CJ7
1
Les données et fonctions immuables sans effets secondaires sont plus faciles à raisonner. Vous pourriez apprécier F # fsharpforfunandprofit.com/posts/is-your-language-unreasonable
Colonel Panic

Réponses:

59

En plus des autres bonnes réponses, j'ajouterai encore une autre raison pour laquelle ne pas mettre la constness de style C dans C #. Tu as dit:

nous marquons le paramètre comme const afin d'être sûr que son état ne sera pas changé dans method.

Si const faisait ça, ce serait génial. Const ne fait pas ça. Le const est un mensonge!

Const ne fournit aucune garantie que je puisse réellement utiliser. Supposons que vous ayez une méthode qui accepte une chose const. Il existe deux auteurs de code: la personne qui écrit l' appelant et la personne qui écrit l' appelé . L'auteur de l'appelé a fait prendre à la méthode un const. Qu'est-ce que les deux auteurs peuvent supposer est invariant pour l'objet?

Rien. L'appelé est libre de rejeter le const et de muter l'objet, de sorte que l'appelant n'a aucune garantie que l'appel d'une méthode qui prend un const ne le mute pas. De même, l'appelé ne peut pas supposer que le contenu de l'objet ne changera pas tout au long de l'action de l'appelé; l'appelé pourrait appeler une méthode de mutation sur un alias non const de l'objet const, et maintenant le soi-disant objet const a changé .

Le const de style C n'offre aucune garantie que l'objet ne changera pas et qu'il est donc cassé. Maintenant, C a déjà un système de type faible dans lequel vous pouvez réinterpréter le cast d'un double en un int si vous le souhaitez vraiment, il ne devrait donc pas être surprenant qu'il ait également un système de type faible par rapport à const. Mais C # a été conçu pour avoir un bon système de type, un système de type où lorsque vous dites "cette variable contient une chaîne" que la variable contient en fait une référence à une chaîne (ou null). Nous ne voulons absolument pas mettre un modificateur "const" de style C dans le système de types parce que nous ne voulons pas que le système de types soit un mensonge . Nous voulons que le système de typage soit fort pour que vous puissiez raisonner correctement sur votre code.

Const en C est une ligne directrice ; cela signifie essentiellement "vous pouvez me faire confiance pour ne pas essayer de muter cette chose". Cela ne devrait pas être dans le système de types ; les éléments du système de types doivent être un fait sur l'objet sur lequel vous pouvez raisonner, et non un guide pour son utilisation.

Maintenant, ne vous méprenez pas; ce n'est pas parce que const en C est profondément brisé que tout le concept est inutile. Ce que j'aimerais voir, c'est une forme d'annotation "const" réellement correcte et utile en C #, une annotation que les humains et les compilateurs pourraient utiliser pour les aider à comprendre le code, et que le runtime pourrait utiliser pour faire des choses comme la paralellisation automatique et autres optimisations avancées.

Par exemple, imaginez si vous pouviez "dessiner une boîte" autour d'un morceau de code et dire "Je vous garantis que ce morceau de code n'effectue aucune mutation dans aucun champ de cette classe" d'une manière qui pourrait être vérifiée par le compilateur. Ou dessinez une boîte qui dit "cette méthode pure mute l'état interne de l'objet mais pas d'une manière qui soit observable en dehors de la boîte". Un tel objet ne peut pas être automatiquement multi- threadé en toute sécurité, mais il peut être mémorisé automatiquement . Il y a toutes sortes d'annotations intéressantes que nous pourrions mettre sur le code qui permettraient des optimisations riches et une compréhension plus profonde. Nous pouvons faire bien mieux que la faible annotation const de style C.

Cependant, je souligne qu'il ne s'agit que de spéculation . Nous n'avons pas l'intention de mettre ce type de fonctionnalité dans une future version hypothétique de C #, s'il y en a une, que nous n'avons pas annoncée d'une manière ou d'une autre. C'est quelque chose que j'aimerais voir, et quelque chose que l'accent à venir sur l'informatique multicœur pourrait exiger, mais rien de tout cela ne devrait être interprété en aucune façon comme une prédiction ou une garantie d'une fonctionnalité particulière ou d'une direction future pour C #.

Maintenant, si ce que vous voulez est simplement une annotation sur la variable locale qui est un paramètre qui dit "la valeur de ce paramètre ne change pas tout au long de la méthode", alors, bien sûr, ce serait facile à faire. Nous pourrions prendre en charge les paramètres locaux et les paramètres "en lecture seule" qui seraient initialisés une fois, et une erreur de compilation pour changer la méthode. La variable déclarée par l'instruction "using" est déjà un tel local; nous pourrions ajouter une annotation facultative à toutes les variables locales et paramètres pour les faire agir comme des variables "utilisant". Cela n'a jamais été une fonctionnalité très prioritaire, donc elle n'a jamais été implémentée.

Eric Lippert
la source
34
Désolé, mais je trouve cet argument très faible, le fait qu'un développeur puisse dire un mensonge ne peut être évité dans aucune langue. void foo (const A & a) qui modifie l'objet a n'est pas différent de int countApples (Basket b) qui renvoient le nombre de poires. C'est juste un bug, un bug qui se compile dans n'importe quel langage.
Alessandro Teruzzi
55
«L'appelé est libre de rejeter le const…» uhhhhh… (° _ °) C'est un argument assez superficiel que vous faites ici. L'appelé est également libre de commencer à écrire des emplacements de mémoire aléatoires avec 0xDEADBEEF. Mais les deux seraient une très mauvaise programmation, et capturés tôt et de manière poignante par n'importe quel compilateur moderne. À l'avenir, lorsque vous rédigez des réponses, veuillez ne pas confondre ce qui est techniquement possible en utilisant les portes dérobées d'un langage avec ce qui est faisable dans le code réel. Des informations biaisées comme celle-ci déroutent les lecteurs moins expérimentés et dégradent la qualité des informations sur le SO.
Slipp D.Thompson
8
«Const in C est une ligne directrice;» Citer une source pour certaines des choses que vous dites aiderait vraiment votre cas ici. Dans mon éducation et mon expérience dans le secteur, la déclaration citée est hogwash - const en C est autant de fonctionnalité intégrée obligatoire que le système de type lui-même - les deux ont des portes dérobées pour les tromper si nécessaire (comme vraiment tout en C) mais sont attendus à utiliser par le livre à 99,99% du code.
Slipp D.Thompson
29
Cette réponse est la raison pour laquelle je déteste parfois le design C #. Les concepteurs de langage sont absolument convaincus qu'ils sont beaucoup plus intelligents que les autres développeurs et essaient de les protéger excessivement des bogues. Il est évident que le mot clé const ne fonctionne qu'au moment de la compilation, car en C ++, nous avons un accès direct à la mémoire et pouvons faire tout ce que nous voulons et avons un certain nombre de façons de contourner la déclaration const. Mais personne ne fait cela dans des situations normales. Au fait, «privé» et «protégé» en C # ne fournissent aucune garantie, car nous pouvons accéder à ces méthodes ou champs en utilisant la réflexion.
BlackOverlord
13
@BlackOverlord: (1) Le désir de concevoir un langage dont les propriétés conduisent naturellement les développeurs au succès plutôt qu'à l'échec est motivé par le désir de créer des outils utiles , et non par la conviction que les concepteurs sont plus intelligents que leurs clients, comme vous le conjecturez. (2) La réflexion ne fait pas partie du langage C #; cela fait partie du runtime; l'accès à la réflexion est limité par le système de sécurité du runtime. Le code de faible confiance n'a pas la capacité d'effectuer une réflexion privée. Les garanties fournies par les modificateurs d'accès concernent le code de faible confiance ; un code de confiance élevée peut tout faire.
Eric Lippert
24

L'une des raisons pour lesquelles il n'y a pas de correction de const en C # est qu'elle n'existe pas au niveau de l'exécution. N'oubliez pas que C # 1.0 n'avait aucune fonctionnalité sauf s'il faisait partie du runtime.

Et plusieurs raisons pour lesquelles le CLR n'a pas de notion de correction de const sont par exemple:

  1. Cela complique le temps d'exécution; de plus, la JVM ne l'avait pas non plus, et le CLR a essentiellement commencé comme un projet pour créer un runtime de type JVM, pas un runtime de type C ++.
  2. S'il existe une correction de const au niveau de l'exécution, il doit y avoir une correction de const dans le BCL, sinon la fonctionnalité est pratiquement inutile en ce qui concerne .NET Framework.
  3. Mais si le BCL nécessite l'exactitude des constats, chaque langage au-dessus du CLR devrait prendre en charge l'exactitude des constats (VB, JavaScript, Python, Ruby, F #, etc.) Cela ne se produira pas.

La correction des constats est à peu près une fonctionnalité de langage présente uniquement en C ++. Donc, cela revient à peu près à la même argumentation quant à la raison pour laquelle le CLR ne nécessite pas d'exceptions vérifiées (qui est une fonctionnalité du langage Java uniquement).

De plus, je ne pense pas que vous puissiez introduire une fonctionnalité de système de type aussi fondamentale dans un environnement géré sans rompre la compatibilité descendante. Donc, ne comptez pas sur l'exactitude de const entrant dans le monde C #.

Ruben
la source
Êtes-vous sûr que JVM ne l'a pas. Comme je le sais, il l'a. Vous pouvez essayer TestMethod public static void (final int val) en Java.
Incognito
2
Final n'est pas vraiment const. Vous pouvez toujours modifier les champs de la référence IIRC, mais pas la valeur / la référence elle-même. (Qui est passé par valeur de toute façon, donc c'est plus une astuce du compilateur pour vous empêcher de changer la valeur du paramètre.)
Ruben
1
votre 3e balle n'est que partiellement vraie. avoir des paramètres const pour les appels BCL ne serait pas un changement de rupture car ils décrivent simplement le contrat plus précisément. "Cette méthode ne changera pas l'état de l'objet" contrairement à "Cette méthode vous oblige à passer une valeur const" qui serait cassante
Rune FS
2
Il est appliqué à l' intérieur de la méthode, donc ce ne serait pas un changement radical de l'introduire dans la BCL.
Rune FS
1
Même si l'exécution ne pouvait pas l'appliquer, il serait utile d'avoir un attribut qui spécifie si une fonction doit être autorisée / attendue pour écrire dans un refparamètre (en particulier, dans le cas des méthodes / propriétés struct, this). Si une méthode indique spécifiquement qu'elle n'écrira pas dans un refparamètre, le compilateur doit autoriser la transmission de valeurs en lecture seule. Inversement, si une méthode déclare qu'elle va écrire this, le compilateur ne doit pas permettre à une telle méthode d'être appelée sur des valeurs en lecture seule.
supercat le
12

Je crois qu'il y a deux raisons pour lesquelles C # n'est pas constant.

Le premier est la compréhensibilité . Peu de programmeurs C ++ comprennent la correction de const. L'exemple simple de const int argest mignon, mais j'ai aussi vu char * const * const arg- un pointeur constant vers des pointeurs constants vers des caractères non constants. La correction des constantes sur les pointeurs vers des fonctions est un tout nouveau niveau d'obfuscation.

La seconde est que les arguments de classe sont des références passées par valeur. Cela signifie qu'il y a déjà deux niveaux de constness à gérer, sans syntaxe évidemment claire . Un point d'achoppement similaire concerne les collections (et les collections de collections, etc.).

Const-correctness est une partie importante du système de type C ++. Il pourrait - en théorie - être ajouté à C # comme quelque chose qui n'est vérifié qu'au moment de la compilation (il n'a pas besoin d'être ajouté au CLR, et n'affecterait pas le BCL à moins que la notion de méthodes membres const ne soit incluse) .

Cependant, je pense que c'est peu probable: la deuxième raison (syntaxe) serait assez difficile à résoudre, ce qui rendrait la première raison (compréhensibilité) encore plus problématique.

Stephen Cleary
la source
1
Vous avez raison, CLR n'a pas vraiment besoin de s'inquiéter à ce sujet, car on peut utiliser modoptet modreqau niveau des métadonnées pour stocker ces informations (comme le fait déjà C + / CLI - voir System.Runtime.CompilerServices.IsConst); et cela pourrait également être étendu aux méthodes const.
Pavel Minaev
Cela en vaut la peine mais doit être fait de manière sécurisée. Le const Deep / Shallow que vous mentionnez ouvre une boîte entière de vers.
user1496062
Dans un C ++ où les références sont en fait manipulées, je peux voir votre argument sur le fait d'avoir deux types de const. Cependant, en C # (où vous devez passer par des portes dérobées pour utiliser des "pointeurs"), il me semble assez clair qu'il existe une signification bien définie pour les types de référence (c'est-à-dire les classes) et une signification bien définie mais différente pour la valeur types (ie primitives, structs)
Assimilater
2
Les programmeurs qui ne peuvent pas comprendre la const-ness ne devraient pas programmer en C ++.
Steve White
5

constsignifie «constante à la compilation» en C #, pas «en lecture seule mais éventuellement modifiable par un autre code» comme en C ++. Un analogue approximatif de C ++ consten C # est readonly, mais celui-ci n'est applicable qu'aux champs. En dehors de cela, il n'y a pas du tout de notion de correction de const dans C # semblable à C ++.

La justification est difficile à dire avec certitude, car il y a beaucoup de raisons potentielles, mais je suppose que ce serait le désir de garder le langage simple avant tout, et les avantages incertains de la mise en œuvre de ce second.

Pavel Minaev
la source
Vous pensez donc que MS considère que le «bruit» a des paramètres const dans les méthodes.
Incognito
1
Je ne sais pas ce qu'est le «bruit» - je préfère l'appeler «rapport coût / bénéfice élevé». Mais, bien sûr, vous aurez besoin de quelqu'un de l'équipe C # pour vous le dire avec certitude.
Pavel Minaev
Donc, comme je le comprends, votre argument est que constness a été laissé en dehors de C # pour rester simple. Pensez-y.
Steve White
2

Cela est possible depuis C # 7.2 (décembre 2017 avec Visual Studio 2017 15.5).

Pour les structures et les types de base uniquement! , pas pour les membres des classes.

Vous devez utiliser inpour envoyer l'argument comme entrée par référence. Voir:

Voir: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

Pour votre exemple:

....
static void TestMethod1(in MyStruct val)
{
    val = new MyStruct;  // Error CS8331 Cannot assign to variable 'in MyStruct' because it is a readonly variable
    val.member = 0;  // Error CS8332 Cannot assign to a member of variable 'MyStruct' because it is a readonly variable
}
....
static void TestMethod2(in int val)
{
    val = 0;  // Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable
}
....
isgoed
la source