¿Por qué C # no permite variables locales de solo lectura?

115

Tener un debate amistoso con un compañero de trabajo sobre esto. Tenemos algunas ideas sobre esto, pero nos preguntamos qué piensa la gente de SO sobre esto.

Brian Genisio
fuente
4
@ColonelPanic C y C ++ tienen variables locales constantes, que puede inicializar con un valor calculado en tiempo de ejecución.
Crashworks
1
JavaScript 2015 (ES6) tiene tipo constante. Por ejemplo, {const myList = [1,2,3]; }. Es una muy buena práctica de programación utilizar esta construcción. Más información: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
andrew.fox
1
Para aquellos interesados, hay una sugerencia de UserVoice para esta función . Actualmente solo tiene 87 votos, así que si desea ver las variables locales de solo lectura, ¡vaya a golpearlo!
Ian Kemp
1
No es solo un problema de idioma. Es un problema de la mayoría en la comunidad de C #, incluidos los gurús de C # mejor calificados, que no se preocupan por la corrección constante y todo lo relacionado con ella. La resistencia es inútil.
Patrick Fromberg
1
Actualización 2017 : vote por la solicitud de función en discusión en el repositorio de diseño de lenguaje C #. github.com/dotnet/csharplang/issues/188
Colonel Panic

Respuestas:

15

Una razón es que no hay soporte CLR para un local de solo lectura. Readonly se traduce en el código de operación de inicio CLR / CLI. Esta bandera solo se puede aplicar a campos y no tiene significado para un local. De hecho, aplicarlo a un local probablemente producirá un código no verificable.

Esto no significa que C # no pueda hacer esto. Pero daría dos significados diferentes a la misma construcción del lenguaje. La versión para locales no tendría un mapeo equivalente a CLR.

JaredPar
fuente
57
En realidad, no tiene nada que ver con el soporte de CLI para la función, porque las variables locales de ninguna manera están expuestas a otros ensamblados. La readonlyCLI debe admitir la palabra clave para campos porque su efecto es visible para otros ensamblados. Todo lo que significaría es que la variable solo tiene una asignación en el método en el momento de la compilación.
Sam Harwell
16
Creo que acaba de cambiar la pregunta a por qué CLR no admite esto en lugar de proporcionar la razón detrás de ello. Permite a los locales constantes, por lo que sería razonable esperar también locales de solo lectura.
Chad Schouggins
9
Un ejemplo de esto son las variables definidas en una declaración using. Son locales ... y de solo lectura (intente asignarlos, C # agregará un error).
Softlion
7
-1 En C ++ no hay soporte para código de máquina const(que en C ++ se parece más a C # readonlyque a C # const, aunque puede desempeñar ambos roles). Sin embargo, C ++ admite constvariables automáticas locales. Por lo tanto, la falta de compatibilidad con CLR para C # readonlypara la variable local es irrelevante.
Saludos y hth. - Alf
5
1. Esta puede ser fácilmente una característica del compilador, como en C ++. El soporte CLR es completamente irrelevante. El ensamblaje de la máquina tampoco lo admite, ¿y qué? 2. (produciría) probablemente un código no verificable - no veo cómo, pero tal vez estoy equivocado. 3. daría dos significados diferentes para la misma construcción del lenguaje - Dudo que alguien vea esto como un problema, ya usingy outestán haciendo exactamente eso y el mundo no se derrumbó.
Lou
66

Creo que es un mal juicio por parte de los arquitectos de C #. El modificador de solo lectura en las variables locales ayuda a mantener la corrección del programa (como afirma) y puede ayudar potencialmente al compilador a optimizar el código (al menos en el caso de otros lenguajes). El hecho de que no esté permitido en C # en este momento es otro argumento de que algunas de las "características" de C # son simplemente una imposición del estilo de codificación personal de sus creadores.

andriej
fuente
11
Estoy de acuerdo con la parte de "guardar al programador de sí mismo", pero en cuanto a ayudar al compilador a optimizar el código, mantengo la postura de que el compilador puede averiguar muy bien si una variable cambia o no en el transcurso del método y se optimiza en consecuencia. de cualquier manera. Colocar un indicador de "solo lectura" antes de algo que el optimizador reconoce de todos modos para ese propósito no beneficia realmente, pero puede inducir a error.
Cornelius
1
@Cornelius Estoy de acuerdo en que hay opiniones de que, en algunos casos, el compilador usa un diagrama de flujo de datos para descubrir la oportunidad de optimización independientemente de cualquier palabra clave / modificadores. Pero evitar que el programador escriba código incorrecto o innecesariamente no optimizado puede abrir esa oportunidad de optimización para el compilador.
shuva
¿No realizan los compiladores modernos la asignación única estática de todos modos? En cuyo caso, es discutible en lo que respecta a las optimizaciones (pero si un compilador admite SSA, significa que también es trivial implementar variables locales de asignación única).
Dai
33

Al abordar la respuesta de Jared, probablemente solo tendría que ser una función en tiempo de compilación: el compilador le prohibiría escribir en la variable después de la declaración inicial (que tendría que incluir una asignación).

¿Puedo ver valor en esto? Potencialmente, pero no mucho, para ser honesto. Si no puede saber fácilmente si se asignará una variable en otro lugar del método, entonces su método es demasiado largo.

Por lo que vale, Java tiene esta característica (usando el finalmodificador) y muy pocas veces la he visto usada excepto en los casos en los que tiene que usarse para permitir que la variable sea capturada por una clase interna anónima, y ​​dónde está usado, me da una impresión de desorden en lugar de información útil.

Jon Skeet
fuente
75
Hay una diferencia entre ver si una variable se modifica o no en su método a simple vista y por el compilador . No veo ninguna objeción a escribir un método, declarar mi intención de no modificar una variable y hacer que el compilador me notifique cuando lo haga accidentalmente (¡quizás con un error tipográfico un mes después)!
A. Rex
50
Por otro lado, en F # todas las variables son de solo lectura de forma predeterminada, y debe usar la palabra clave 'mutable' si desea poder cambiarlas. Dado que F # es un lenguaje .NET, imagino que realiza la verificación en tiempo de compilación que describe.
Joel Mueller
2
@ A.Rex: La pregunta es realmente si el beneficio de hacer que el compilador haga esa verificación vale la pena la "pelusa" adicional al leer el código y no preocuparse por él.
Jon Skeet
3
FWIW, Scala distingue los valores readonly/ locales finalde las variables con sus palabras clave valy var. En el código de Scala, los mensajes de correo electrónico locales valse utilizan con mucha frecuencia (y, de hecho, se prefieren a los varmensajes de correo electrónico locales ). Sospecho que las razones principales por las que el finalmodificador no se usa con más frecuencia en Java son a) el desorden yb) la pereza.
Aaron Novstrup
4
Para las variables locales que no se utilizan en cierres, readonly no sería demasiado importante. Por otro lado, para las variables locales que se utilizan en cierres, readonlyen muchos casos permitiría al compilador generar código más eficiente. Actualmente, cuando la ejecución entra en un bloque que contiene un cierre, el compilador debe crear un nuevo objeto de montón para las variables cerradas, incluso si nunca se ejecuta ningún código que usaría el cierre . Si una variable fuera de solo lectura, el código fuera del cierre podría usar una variable normal; solo cuando se crea un delegado para el cierre ...
supercat
30

El equipo de diseño de C # 7 discutió brevemente una propuesta de solo lectura local y parámetros . De C # Design Meeting Notes para el 21 de enero de 2015 :

Los parámetros y los valores locales pueden ser capturados por lambdas y, por lo tanto, acceder a ellos simultáneamente, pero no hay forma de protegerlos de problemas de estado mutuo compartido: no pueden ser de solo lectura.

En general, la mayoría de los parámetros y muchos locales nunca deben asignarse después de obtener su valor inicial. Permitir solo lectura en ellos expresaría esa intención con claridad.

Un problema es que esta característica puede ser una "molestia atractiva". Mientras que lo "correcto" sería casi siempre hacer que los parámetros y los locales sean de solo lectura, saturaría el código de manera significativa.

Una idea para aliviar esto en parte es permitir que la combinación de solo lectura var en una variable local se contraiga a val o algo así. De manera más general, podríamos intentar pensar simplemente en una palabra clave más corta que la establecida readonly para expresar el readonly-ness.

La discusión continúa en el repositorio de diseño del lenguaje C #. Vote para mostrar su apoyo. https://github.com/dotnet/csharplang/issues/188

Coronel Panic
fuente
también podría hacer que readonly sea el valor predeterminado (a través de alguna opción o palabra clave o lo que sea). la mayoría de las variables y parámetros deben ser de solo lectura, con solo unos pocos modificables. y minimizar el número que se puede escribir es generalmente algo bueno.
Dave Cousineau
12

Es un descuido del diseñador del lenguaje C #. F # tiene la palabra clave val y se basa en CLR. No hay ninguna razón por la que C # no pueda tener la misma característica de idioma.

Derek Liang
fuente
7

¡Yo era ese compañero de trabajo y no era amigable! (es una broma)

No eliminaría la función porque es mejor escribir métodos cortos. Es un poco como decir que no deberías usar hilos porque son difíciles. Dame el cuchillo y déjame ser responsable de no cortarme.

Personalmente, quería otra palabra clave de tipo "var" como "inv" (invariante) o "rvar" para evitar el desorden. He estado estudiando F # últimamente y encuentro atractivo lo inmutable.

Nunca supe que Java tenía esto.

Miguel
fuente
5

Me gustaría variables de solo lectura locales de la misma manera que me gustan las variables de const locales . Pero tiene menos prioridad que otros temas.
Tal vez su prioridad sea ​​la misma razón por la que los diseñadores de C # no implementan (¡todavía!) Esta característica. Pero debería ser fácil (y compatible con versiones anteriores) admitir variables de solo lectura locales en versiones futuras.

brgerner
fuente
2

Solo lectura significa que el único lugar donde se puede establecer la variable de instancia es en el constructor. Cuando se declara una variable localmente, no tiene una instancia (solo está en el alcance) y el constructor no puede tocarla.

Jason Punyon
fuente
6
Ese es el significado actual de 'solo lectura' en C #, pero esa no es la cuestión. 'solo lectura' tiene un significado en inglés que parece tener una aplicación intuitiva para una variable local: no puede escribir en ella (después de que se haya inicializado). Eso se parece mucho al significado cuando se aplica a variables de instancia, entonces, ¿por qué (como en la justificación, creo) no podemos aplicarlo a variables locales?
Spike0xff
0

Lo sé, esto no responde al por qué de tu pregunta. De todos modos, aquellos que lean esta pregunta podrían apreciar el código a continuación.

Si realmente le preocupa dispararse a sí mismo en el pie al anular una variable local que solo debe establecerse una vez, y no desea convertirla en una variable más accesible globalmente, podría hacer algo como esto.

    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);
        }
    }

Uso de ejemplo:

        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

Quizás no menos código, rvar rInt = 5pero funciona.

Mike de Klerk
fuente
Eso no ayuda aquí. Este problema con la variable 'var' es: {var cinco = 5 cinco = 6; Afirmar eso (cinco == 5)}
Murray
0

Puede declarar variables locales de solo lectura en C #, si está utilizando el compilador interactivo de 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)

También puede declarar variables locales de solo lectura en el .csxformato de script.

Coronel Panic
fuente
5
Según el mensaje de error, messageaquí no hay una variable, se compila en un campo. Esto no es quisquilloso porque la distinción también existe claramente en C # interactivo: int x; Console.WriteLine(x)es C # interactivo legal (porque xes un campo e implícitamente inicializado) pero void foo() { int x; Console.WriteLine(x); }no lo es (porque xes una variable y se usa antes de ser asignada). Además, Expression<Func<int>> y = x; ((MemberExpression) y.Body).Member.MemberTyperevelará que xrealmente es un campo y no una variable local.
Jeroen Mostert
0

c # ya tiene una var de solo lectura, aunque en una sintaxis algo diferente:

Considere las siguientes líneas:

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

Comparar con:

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

Es cierto que la primera solución podría ser menos código para escribir. Pero el segundo fragmento hará que la lectura sea explícita al hacer referencia a la variable.

Wolfgang Grinfeld
fuente
Eso no es "solo lectura". Esa es una función local que devuelve una variable capturada ... mucha sobrecarga innecesaria y sintaxis desagradable que ni siquiera hace que la variable subyacente sea de solo lectura, desde el punto de vista del acceso al código. Además, "mutable" por lo general se aplica a los objetos, las variables locales no consideran ..: readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");.
user2864740
-2

utilizar const palabra clave para hacer una variable de solo lectura.

referencia: 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
fuente
1
Estamos interesados ​​en el estilo JavaScript, constdonde la variable solo se puede asignar durante su inicialización, no en el estilo csharp, constdonde solo se pueden usar expresiones en tiempo de compilación. Por ejemplo, no puede hacerlo, const object c = new object();pero un readonlylocal le permitiría hacerlo.
binki
-5

Creo que eso se debe a que es posible que nunca se llame a una función que tiene una variable de solo lectura, y es probable que haya algo acerca de que se salga del alcance, y ¿cuándo debería hacerlo?

Scottm
fuente