¿Por qué C#limita el conjunto de tipos que pueden declararse como const?
constants compiler-errors (6)
El error del compilador CS0283 indica que solo los tipos básicos de POD (así como las cadenas, las enumeraciones y las referencias nulas) se pueden declarar como const
. ¿Alguien tiene una teoría sobre el fundamento de esta limitación? Por ejemplo, sería bueno poder declarar valores const de otros tipos, como IntPtr.
Creo que el concepto de const
es en realidad azúcar sintáctico en C #, y que simplemente reemplaza cualquier uso del nombre con el valor literal. Por ejemplo, dada la siguiente declaración, cualquier referencia a Foo sería reemplazada por "foo" en tiempo de compilación.
const string Foo = "foo";
Esto descartaría cualquier tipo mutable, ¿entonces tal vez eligieron esta limitación en lugar de tener que determinar en tiempo de compilación si un tipo dado es mutable?
¿Alguien tiene una teoría sobre el fundamento de esta limitación?
Si se permite que sea solo una teoría, mi teoría es que los valores de const de los tipos primitivos pueden expresarse en parámetros de código de operación literales en el MSIL ... pero los valores de otros tipos no primitivos no pueden, porque MSIL no tiene el sintaxis para expresar el valor de un tipo definido por el usuario como un literal.
Me parece que solo los tipos de valores se pueden expresar como una constante (con la excepción de cadenas, que se encuentra entre el valor y el tipo de objeto).
Está bien para mí: los objetos (referencias) deben asignarse en el montón, pero las constantes no se asignan en absoluto (ya que se reemplazan en tiempo de compilación).
las conste se limitan a números y cadenas en C # porque el compilador reemplaza la variable con el valor literal en MSIL. En otras palabras, cuando escribes:
const string myName = "Bruce Wayne";
if (someVar == myName)
{
...
}
en realidad es tratado como
if (someVar == "Bruce Wayne")
{
...
}
y sí, el compilador de C # es lo suficientemente inteligente como para tratar al operador de igualdad (==) en cadenas como
string1.Equals(string2)
Creo que el concepto de const es en realidad azúcar sintáctico en C #, y que simplemente reemplaza cualquier uso del nombre con el valor literal
¿Qué hace el compilador con objetos const en otros idiomas?
Puede usar readonly para tipos mutables que me serán evaluados en tiempo de ejecución. Ver este artículo para las diferencias.
De la especificación C #, capítulo 10.4 - Constantes :
(10.4 en la especificación C # 3.0, 10.3 en la versión en línea para 2.0)
Una constante es un miembro de clase que representa un valor constante: un valor que puede calcularse en tiempo de compilación.
Esto básicamente dice que solo puede usar expresiones que constan únicamente de literales. Cualquier llamada a cualquier método, constructores (que no pueden ser representados como literales puros de IL) no pueden ser utilizados, ya que no hay forma de que el compilador realice esa ejecución y, por lo tanto, calcule los resultados, en tiempo de compilación. Además, dado que no hay forma de etiquetar un método como invariante (es decir, hay una correspondencia de uno a uno entre la entrada y la salida), la única forma de que el compilador haga esto sería analizar el IL para ver si depende de cosas que no sean los parámetros de entrada, casos especiales manejan algunos tipos (como IntPtr), o simplemente no permiten cada llamada a cualquier código.
IntPtr, por ejemplo, aunque es un tipo de valor, sigue siendo una estructura y no uno de los literales incorporados. Como tal, cualquier expresión que use un IntPtr necesitará llamar al código en la estructura IntPtr, y esto es lo que no es legal para una declaración constante.
El único ejemplo legal de tipo de valor constante que puedo pensar sería uno que se inicializa con ceros con solo declararlo, y eso no es útil.
En cuanto a cómo el compilador trata / usa las constantes, utilizará el valor calculado en lugar del nombre constante en el código.
Por lo tanto, tiene el siguiente efecto:
- No se compila ninguna referencia al nombre de la constante original, la clase en la que se declaró o el espacio de nombres en el código de esta ubicación.
- Si descompila el código, tendrá números mágicos, simplemente porque la "referencia" original a la constante no está presente, como se mencionó anteriormente, solo el valor de la constante
- El compilador puede usar esto para optimizar, o incluso eliminar, código innecesario. Por ejemplo,
if (SomeClass.Version == 1)
, cuando SomeClass.Version tiene el valor de 1, de hecho eliminará el enunciado if y mantendrá el bloque de código en ejecución. Si el valor de la constante no es 1, entonces toda la instrucción if y su bloque serán eliminados. - Dado que el valor de una constante se compila en el código, y no una referencia a la constante, el uso de constantes de otros ensambles no actualizará automágicamente el código compilado de ninguna manera si el valor de la constante cambia (¡lo cual no debería ser así!)
En otras palabras, con la siguiente situación:
- El ensamblaje A contiene una constante llamada "Versión", que tiene un valor de 1
- El ensamblaje B contiene una expresión que analiza el número de versión del ensamblaje A de esa constante y lo compara con 1, para asegurarse de que puede funcionar con el ensamblaje
- Alguien modifica el ensamblaje A, incrementa el valor de la constante a 2 y reconstruye A (pero no B)
En este caso, el conjunto B, en su forma compilada, aún comparará el valor de 1 a 1, porque cuando se compiló B, la constante tenía el valor 1.
De hecho, si ese es el único uso de algo del ensamblaje A en el ensamblaje B, el ensamblado B se compilará sin una dependencia en el ensamblaje A. La ejecución del código que contiene esa expresión en el ensamblaje B no cargará el ensamblaje A.
Las constantes solo deberían usarse para cosas que nunca cambiarán. Si se trata de un valor que podría o va a cambiar en algún momento en el futuro, y no puede garantizar que todos los demás conjuntos se reconstruyan simultáneamente, un campo de solo lectura es más apropiado que una constante.
Entonces esto está bien:
- Const pública Int32 NumberOfDaysInAWeekInGregorianCalendar = 7;
- const público Int32 NumberOfHoursInADayOnEarth = 24;
mientras que esto no es:
- public const Int32 AgeOfProgrammer = 25;
- public const String NameOfLastProgrammerThatModifiedAssembly = "Joe Programmer";
Editar 27 de mayo de 2016
OK, acabo de recibir un voto positivo, así que volví a leer mi respuesta aquí y esto en realidad es un poco incorrecto.
Ahora, la intención de la especificación del lenguaje C # es todo lo que escribí arriba. Se supone que no debes usar algo que no se pueda representar con un literal como const
.
Pero puedes? Bueno, sí....
Echemos un vistazo al tipo decimal
.
public class Test
{
public const decimal Value = 10.123M;
}
Veamos cómo es realmente esta clase cuando se mira con ildasm:
.field public static initonly valuetype [mscorlib]System.Decimal X
.custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(int8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 64 00 00 00 00 00 )
Déjame desglosarlo por ti:
.field public static initonly
corresponde a:
public static readonly
Así es, una const decimal
es en realidad un readonly decimal
.
El verdadero problema aquí es que el compilador usará ese DecimalConstantAttribute
para trabajar su magia.
Ahora, esta es la única magia que conozco con el compilador de C #, pero pensé que valía la pena mencionarla.
En resumen, todos los tipos simples, enumeraciones y cadenas son inmutables pero un Struct por ejemplo no lo es. Puede tener un Struct con estado mutable (campos, propiedades, incluso referencias a Tipos de Referencia). Por lo tanto, el compilador no puede asegurarse (en tiempo de compilación) de que el estado interno de una variable Struct no se puede cambiar. Por lo tanto, el compilador debe asegurarse de que un tipo sea, por definición, inmutable para ser utilizado en una expresión constante.