Pourquoi C # interdit-il les variables locales en lecture seule?

115

Avoir un débat amical avec un collègue à ce sujet. Nous avons quelques réflexions à ce sujet, mais nous nous demandons ce que la foule SO en pense?

Brian Genisio
la source
4
@ColonelPanic C et C ++ ont des variables locales const, que vous pouvez initialiser avec une valeur calculée à l'exécution.
Crashworks
1
JavaScript 2015 (ES6) a le type const. Par exemple {const maListe = [1,2,3]; }. C'est une très bonne pratique de programmation d'utiliser cette construction. Plus d'informations: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
andrew.fox
1
Pour ceux qui sont intéressés, il existe une suggestion UserVoice pour cette fonctionnalité . Il ne siège actuellement qu'à 87 votes, donc si vous voulez voir les variables locales en lecture seule, allez-y!
Ian Kemp
1
Ce n'est pas seulement un problème de langue. C'est un problème d'une majorité dans la communauté C #, y compris les gourous C # les mieux notés, de ne pas se soucier de l'exactitude des constats et de tout ce qui y est lié. La résistance est futile.
Patrick Fromberg le
1
Mise à jour 2017 : veuillez voter pour la demande de fonctionnalité en cours de discussion dans le repo C # Language Design! github.com/dotnet/csharplang/issues/188
Colonel Panic

Réponses:

15

L'une des raisons est qu'il n'y a pas de support CLR pour un local en lecture seule. Readonly est traduit dans l'opcode initonly CLR / CLI. Cet indicateur ne peut être appliqué qu'aux champs et n'a aucune signification pour un local. En fait, l'appliquer à un local produira probablement un code invérifiable.

Cela ne signifie pas que C # ne peut pas faire cela. Mais cela donnerait deux significations différentes à la même construction de langage. La version pour les locaux n'aurait pas de mappage équivalent CLR.

JaredPar
la source
57
Cela n'a en fait rien à voir avec la prise en charge de la fonctionnalité par CLI, car les variables locales ne sont en aucun cas exposées à d'autres assemblys. Le readonlymot-clé pour les champs doit être pris en charge par l'interface de ligne de commande car son effet est visible par les autres assemblys. Tout cela signifierait que la variable n'a qu'une seule affectation dans la méthode au moment de la compilation.
Sam Harwell
16
Je pense que vous venez de poser la question de savoir pourquoi le CLR n'appuie pas cela plutôt que de fournir le rationnel derrière cela. Il autorise les locaux const, il serait donc raisonnable de s'attendre également à des locaux en lecture seule.
Chad Schouggins
9
Un exemple de ceci est la variable définie dans une instruction using. Ils sont locaux ... et en lecture seule (essayez de les affecter, C # ajoutera une erreur).
Softlion
7
-1 En C ++ il n'y a pas de support de code machine pour const(qui en C ++ ressemble plus à C # readonlyqu'à C # const, bien qu'il puisse jouer les deux rôles). Pourtant, C ++ prend en charge constla variable automatique locale. Par conséquent, le manque de support CLR pour un C # readonlypour une variable locale n'est pas pertinent.
Acclamations et hth. - Alf
5
1. Cela peut facilement être une fonctionnalité du compilateur, comme en C ++. Le support CLR est totalement hors de propos. L'assemblage de la machine ne le supporte pas non plus, et alors? 2. (cela produirait) probablement du code non vérifiable - je ne vois pas comment, mais peut-être que je me trompe. 3. cela donnerait deux significations différentes à la même construction de langage - je doute que quiconque verrait cela comme un problème, car usinget outfont exactement cela et le monde ne s'est pas effondré.
Lou
66

Je pense que c'est un mauvais jugement de la part des architectes C #. Le modificateur readonly sur les variables locales aide à maintenir l'exactitude du programme (tout comme les assertions) et peut potentiellement aider le compilateur à optimiser le code (au moins dans le cas d'autres langages). Le fait qu'il soit interdit en C # pour le moment, est un autre argument selon lequel certaines des "fonctionnalités" de C # sont simplement une application du style de codage personnel de ses créateurs.

Andriej
la source
11
Je suis d'accord sur la partie "sauver le programmeur de lui-même", mais pour ce qui est d'aider le compilateur à optimiser le code, je soutiens que le compilateur peut très bien savoir si une variable change ou non au cours de la méthode et optimise en conséquence d'une manière ou d'une autre. Placer un indicateur «lecture seule» avant quelque chose que l'optimiseur reconnaît de toute façon à cette fin ne profite pas vraiment, mais peut néanmoins induire en erreur.
Cornelius
1
@Cornelius Je suis d'accord qu'il y a des opinions selon lesquelles, dans certains cas, le compilateur utilise un diagramme de flux de données pour déterminer l'opportunité d'optimisation quel que soit le mot clé / modificateur. Mais sauver le programmeur de lui-même de l'écriture de code incorrect et autrement inutilement non optimisé peut ouvrir cette opportunité d'optimisation pour le compilateur.
shuva
Les compilateurs modernes n'effectuent-ils pas de toute façon l'attribution statique unique? Dans ce cas, il est inutile en ce qui concerne les optimisations (mais si un compilateur prend en charge SSA, cela signifie qu'il est également trivial d'implémenter des variables locales assign-once).
Dai
33

Pour répondre à la réponse de Jared, il faudrait probablement juste une fonctionnalité de compilation - le compilateur vous interdirait d'écrire dans la variable après la déclaration initiale (qui devrait inclure une affectation).

Puis-je voir la valeur à cela? Potentiellement - mais pas beaucoup, pour être honnête. Si vous ne pouvez pas facilement dire si une variable va être affectée ailleurs dans la méthode, alors votre méthode est trop longue.

Pour ce que ça vaut, Java a cette fonctionnalité (en utilisant le finalmodificateur) et je l'ai très rarement vue utilisée sauf dans les cas où elle doit être utilisée pour permettre à la variable d'être capturée par une classe interne anonyme - et où elle se trouve utilisé, cela me donne une impression de fouillis plutôt que d'informations utiles.

Jon Skeet
la source
75
Il y a une différence entre voir si une variable est modifiée ou non dans votre méthode à vue et par le compilateur . Je ne vois aucune objection à écrire une méthode, à déclarer mon intention de ne pas modifier une variable et à demander au compilateur de me prévenir lorsque je le fais accidentellement (peut-être avec une faute de frappe un mois plus tard)!
A. Rex le
50
D'un autre côté, en F # toutes les variables sont en lecture seule par défaut, et vous devez utiliser le mot-clé «mutable» si vous voulez pouvoir les changer. Puisque F # est un langage .NET, j'imagine qu'il effectue la vérification à la compilation que vous décrivez.
Joel Mueller le
2
@ A.Rex: La question est vraiment de savoir si l'avantage d'amener le compilateur à faire cette vérification vaut le "fluff" supplémentaire lors de la lecture du code et ne s'en soucie pas réellement.
Jon Skeet le
3
FWIW, Scala distingue les valeurs locales readonly/ finaldes variables avec ses mots val- varclés et . Dans le code Scala, les locaux valsont très fréquemment utilisés (et sont, en fait, préférés aux locaux var). Je soupçonne que les principales raisons pour lesquelles le finalmodificateur n'est pas utilisé plus fréquemment en Java sont a) l'encombrement et b) la paresse.
Aaron Novstrup
4
Pour les variables locales qui ne sont pas utilisées dans les fermetures, readonlyce ne serait pas trop important. D'un autre côté, pour les variables locales qui sont utilisées dans les fermetures, readonlylaisserait dans de nombreux cas le compilateur générer du code plus efficace. Actuellement, quand l'exécution entre dans un bloc qui contient une fermeture, le compilateur doit créer un nouvel objet de tas pour les variables fermées, même si aucun code qui utiliserait la fermeture n'est jamais exécuté . Si une variable était en lecture seule, le code en dehors de la fermeture pourrait utiliser une variable normale; seulement quand un délégué est créé pour la fermeture ...
supercat
30

Une proposition en lecture seule pour les sections locales et les paramètres a été brièvement discutée par l'équipe de conception C # 7. À partir des notes de la réunion C # Design du 21 janvier 2015 :

Les paramètres et les locaux peuvent être capturés par des lambdas et ainsi accédés simultanément, mais il n'y a aucun moyen de les protéger des problèmes d'état partagé-mutuel: ils ne peuvent pas être en lecture seule.

En général, la plupart des paramètres et de nombreux paramètres locaux ne sont jamais destinés à être affectés après avoir obtenu leur valeur initiale. Autoriser la lecture seule sur eux exprimerait clairement cette intention.

Un problème est que cette fonctionnalité peut être une «nuisance intéressante». Alors que la «bonne chose» à faire serait presque toujours de rendre les paramètres et les sections locales en lecture seule, cela encombrerait considérablement le code pour le faire.

Une idée pour atténuer partiellement cela est de permettre à la combinaison de var en lecture seule sur une variable locale d'être contractée en val ou quelque chose de court comme ça. Plus généralement, nous pourrions essayer de penser simplement à un mot-clé plus court que le readonly établi pour exprimer le readonly-ness.

La discussion se poursuit dans le référentiel C # Language Design. Votez pour montrer votre soutien. https://github.com/dotnet/csharplang/issues/188

Colonel Panic
la source
pourrait également faire de readonly la valeur par défaut (via une option ou un mot clé ou autre). la plupart des variables et des paramètres doivent être en lecture seule, avec seulement quelques-uns inscriptibles. et minimiser le nombre inscriptible est généralement une bonne chose.
Dave Cousineau le
12

C'est un oubli pour le concepteur de langage c #. F # a le mot clé val et il est basé sur CLR. Il n'y a aucune raison pour laquelle C # ne peut pas avoir la même fonctionnalité de langage.

Derek Liang
la source
7

J'étais ce collègue et ce n'était pas sympathique! (je rigole)

Je n'éliminerais pas la fonctionnalité car il vaut mieux écrire des méthodes courtes. C'est un peu comme dire que vous ne devriez pas utiliser de threads parce qu'ils sont durs. Donnez-moi le couteau et laissez-moi être responsable de ne pas me couper.

Personnellement, je voulais un autre mot-clé de type "var" comme "inv" (invarient) ou "rvar" pour éviter l'encombrement. J'ai récemment étudié F # et je trouve la chose immuable attrayante.

Je n'ai jamais su que Java avait ça.

Mike
la source
5

Je voudrais des variables locales en lecture seule de la même manière que j'aime les variables const locales . Mais il a moins de priorité que d'autres sujets.
Peut-être que sa priorité est la même raison pour les concepteurs C # de ne pas (encore!) Implémenter cette fonctionnalité. Mais il devrait être facile (et rétrocompatible) de prendre en charge les variables locales en lecture seule dans les versions futures.

Brgerner
la source
2

Readonly signifie que le seul endroit où la variable d'instance peut être définie est dans le constructeur. Lors de la déclaration d'une variable localement, elle n'a pas d'instance (elle est juste dans la portée), et elle ne peut pas être touchée par le constructeur.

Jason Punyon
la source
6
C'est la signification actuelle de «lecture seule» en C #, mais ce n'est pas la question. «lecture seule» a une signification anglaise qui semble avoir une application intuitive à une variable locale: vous ne pouvez pas y écrire (après son initialisation). Cela ressemble beaucoup à la signification appliquée aux variables d'instance, alors pourquoi (comme dans la justification, je pense) ne pouvons-nous pas l'appliquer aux variables locales?
Spike0xff
0

Je sais, cela ne répond pas au pourquoi de votre question. Quoi qu'il en soit, ceux qui liront cette question apprécieront peut-être le code ci-dessous.

Si vous êtes vraiment préoccupé par le fait de vous tirer une balle dans le pied lors de la substitution d'une variable locale qui ne devrait être définie qu'une seule fois, et que vous ne voulez pas en faire une variable plus accessible globalement, vous pouvez faire quelque chose comme ça.

    public class ReadOnly<T>
    {
        public T Value { get; private set; }

        public ReadOnly(T pValue)
        {
            Value = pValue;
        }

        public static bool operator ==(ReadOnly<T> pReadOnlyT, T pT)
        {
            if (object.ReferenceEquals(pReadOnlyT, null))
            {
                return object.ReferenceEquals(pT, null);
            }
            return (pReadOnlyT.Value.Equals(pT));
        }

        public static bool operator !=(ReadOnly<T> pReadOnlyT, T pT)
        {
            return !(pReadOnlyT == pT);
        }
    }

Exemple d'utilisation:

        var rInt = new ReadOnly<int>(5);
        if (rInt == 5)
        {
            //Int is 5 indeed
        }
        var copyValueOfInt = rInt.Value;
        //rInt.Value = 6; //Doesn't compile, setter is private

Peut-être pas moins de code rvar rInt = 5mais ça marche.

Mike de Klerk
la source
Cela n'aide pas ici. Ce problème avec la variable «var» est: {var cinq = 5 cinq = 6; Assert.That (cinq == 5)}
Murray
0

Vous pouvez déclarer des variables locales en lecture seule en C #, si vous utilisez le compilateur interactif C # csi:

>"C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe"
Microsoft (R) Visual C# Interactive Compiler version 1.3.1.60616
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> readonly var message = "hello";
> message = "goodbye";
(1,1): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

Vous pouvez également déclarer des variables locales en lecture seule au .csxformat script.

Colonel Panic
la source
5
Selon le message d'erreur, ce messagen'est pas une variable ici, il est compilé dans un champ. Ce n'est pas compliqué car la distinction existe également clairement dans le C # interactif: int x; Console.WriteLine(x)est du C # interactif légal (parce que xc'est un champ et implicitement initialisé) mais void foo() { int x; Console.WriteLine(x); }ne l'est pas (parce que xc'est une variable et utilisée avant d'être assignée). En outre, Expression<Func<int>> y = x; ((MemberExpression) y.Body).Member.MemberTyperévélera que xc'est vraiment un champ et non une variable locale.
Jeroen Mostert
0

c # a déjà une variable en lecture seule, bien que dans une syntaxe quelque peu différente:

Considérez les lignes suivantes:

var mutable = myImmutableCalculationMethod();
readonly var immutable = mutable; // not allowed in C# 8 and prior versions
return immutable;

Comparer avec:

var mutable = myImmutableCalculationMethod();
string immutable() => mutable; // allowed in C# 7
return immutable();

Certes, la première solution pourrait éventuellement être moins de code à écrire. Mais le 2ème extrait rendra la lecture seule explicite, lors du référencement de la variable.

Wolfgang Grinfeld
la source
Ce n'est pas «en lecture seule». C'est une fonction locale renvoyant une variable capturée ... donc beaucoup de surcharge inutile et de syntaxe laide qui ne rend même pas la variable sous-jacente en lecture seule, du point de vue de l'accès au code. , « Mutable » applique aussi généralement à des objets, et non variables locales .. considérer: readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");.
user2864740
-2

utilisez le constmot-clé pour créer une variable en lecture seule.

référence: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const

public class SealedTest
{
    static void Main()
    {
        const int c = 707;
        Console.WriteLine("My local constant = {0}", c);
    }
}
Derek Liang
la source
1
Nous sommes intéressés par le style JavaScript constoù la variable ne peut être affectée que lors de son initialisation - pas par le style csharp constoù seules les expressions au moment de la compilation peuvent être utilisées. Par exemple, vous ne pouvez pas faire const object c = new object();mais un readonlylocal vous le permettrait.
binki
-5

Je pense que c'est parce qu'une fonction qui a une variable en lecture seule peut ne jamais être appelée, et il y a probablement quelque chose à ce sujet qui sort du cadre, et quand en auriez-vous besoin?

Scottm
la source