Desde el C# specification, chapter 10.4 - Constants:
(10,4 en la especificación C# 3.0, 10.3 en la versión en línea de 2,0)
Una constante es un miembro de la clase que representa un valor constante: un valor que puede ser computado 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,existe un mapeo uno a uno entre entrada y salida), la única forma para que el compilador haga esto sería analizar el IL para ver si depende de cosas que no sean los parámetros de entrada, caso especial manejar algunos tipos (como IntPtr), o simplemente no permitir todas las llamadas 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 de tipo de valor de constante legal 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, usará el valor calculado en lugar del nombre constante en el código.
Por lo tanto, tiene el siguiente efecto:
- No se hace referencia al nombre de la constante original, la clase fue declarado en, o espacio de nombres, se compila en el código en esta ubicación
- Si descompilación código, tendrá números mágicos, simplemente porque la "referencia" original a la constante es, como se mencionó anteriormente, no presente, 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 es 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 cambiara (lo cual debería no)
En otras palabras, con el siguiente escenario:
- Asamblea a, contiene una constante llamada "Versión", tiene un valor de 1
- Asamblea B, contiene una expresión que analiza el número de versión del conjunto A a partir de esa constante a nd compara con 1, para asegurarse de que puede trabajar con el conjunto
- Alguien modifica conjunto A, aumentando el valor de la constante a 2, y reconstruye A (pero no B)
En este caso, El ensamblado 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 del ensamble A en conjunto B, conjunto B se compilará sin dependencia del conjunto A. La ejecución del código que contiene esa expresión en el conjunto B no cargará el conjunto A.
Las constantes solo deben 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.
Así que esto es aceptable:
- const pública Int32 NumberOfDaysInAWeekInGregorianCalendar = 7;
- public const Int32 NumberOfHoursInADayOnEarth = 24;
mientras que esto no es:
- const pública Int32 AgeOfProgrammer = 25;
- public const String NameOfLastProgrammerThatModifiedAssembly = "Joe Programmer";
edición puede 27o el año 2016
bien, acaba de recibir un upvote, por lo que volver a leer mi respuesta aquí y esto es en realidad un poco mal.
Ahora, la intención de la especificación de lenguaje C# es todo lo que escribí anteriormente. No debes usar algo que no se pueda representar con un literal como const
.
¿Pero puede? Bueno, sí ...
Echemos un vistazo al tipo decimal
.
public class Test
{
public const decimal Value = 10.123M;
}
Echemos un vistazo a lo que esta clase se parece realmente si 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)
Vamos a dividirla para usted:
.field public static initonly
corresponde a:
public static readonly
Correcto, un const decimal
es en realidad un readonly decimal
.
El verdadero problema aquí es que el compilador usará ese DecimalConstantAttribute
para hacer su magia.
Ahora, esta es la única magia que conozco con el compilador C#, pero pensé que valía la pena mencionarla.
Obviamente, no puede hacer esto, pero * hablando hipotéticamente *, supongamos que teníamos un método estático de devolución de valor que estaba marcado con el atributo [Puro] y el compilador realmente lo impuso (no lo hace actualmente). ¿Podrían los diseñadores del lenguaje al menos * teóricamente * permitir que se inicialice una const con método puro de retorno de tipo de valor estático (con la aplicación real de atributo [Pure]) siempre que los argumentos no sean más que literales? Parece que podrían simplemente ejecutarlo con argumentos literalmente, obtener el resultado que obviamente no va a cambiar y sub en el valor. ¿Estoy pasando por alto algo? –
'En este caso, el conjunto B, en su forma compilada, aún comparará el valor de 1 a 1, porque cuando B se compiló, la constante tenía el valor 1' - aún puede hacer lo mismo con' const int público AssemblyVersion = 1' en cualquier versión de C# en la actualidad, por lo que no hay nada malo con * structures * en sí mismos –