Из спецификации C #, глава 10.4 - Константы :
(10,4 в спецификации C # 3.0, 10,3 в онлайн-версии для 2.0)
Константа - это член класса, представляющий постоянное значение: значение, которое можно вычислить во время компиляции.
Это в основном говорит о том, что вы можете использовать только выражения, которые состоят исключительно из литералов. Любые вызовы любых методов, конструкторов (которые не могут быть представлены как чистые литералы IL) не могут быть использованы, поскольку у компилятора нет способа выполнить это выполнение и, таким образом, вычислить результаты во время компиляции. Кроме того, поскольку нет способа пометить метод как инвариантный (т. Е. Существует взаимно-однозначное сопоставление между входом и выходом), единственный способ для компилятора сделать это - проанализировать IL, чтобы увидеть, это зависит от других факторов, кроме входных параметров, особого случая, обрабатывающего некоторые типы (например, IntPtr), или просто запрещающего каждый вызов любого кода.
IntPtr, например, хотя и является типом значения, но все же является структурой, а не одним из встроенных литералов. Таким образом, любое выражение, использующее IntPtr, должно вызывать код в структуре IntPtr, и это является недопустимым для объявления констант.
Единственный допустимый пример типа значения константы, о котором я могу подумать, это пример, который инициализируется нулями, просто объявляя его, и это вряд ли полезно.
Что касается того, как компилятор обрабатывает / использует константы, он будет использовать вычисленное значение вместо имени константы в коде.
Таким образом, вы получаете следующий эффект:
- Никакая ссылка на исходное имя константы, класс, в котором она была объявлена, или пространство имен, не скомпилировано в код в этом месте
- Если вы декомпилируете код, он будет содержать магические числа просто потому, что первоначальная «ссылка» на константу, как упоминалось выше, отсутствует, только значение константы
- Компилятор может использовать это для оптимизации или даже удаления ненужного кода. Например,
if (SomeClass.Version == 1)
, когда SomeClass.Version имеет значение 1, фактически удалит оператор if и сохранит выполняемый блок кода. Если значение константы не равно 1, весь оператор if и его блок будут удалены.
- Поскольку значение константы компилируется в код, а не является ссылкой на константу, использование констант из других сборок не приведет к автоматическому обновлению скомпилированного кода в любом случае, если значение константы должно измениться (что следует нет!)
Другими словами, по следующему сценарию:
- Сборка A, содержит константу с именем «Version», имеющую значение 1
- Сборка B, содержит выражение, которое анализирует номер версии сборки A по этой константе и сравнивает ее с 1, чтобы убедиться, что она может работать со сборкой
- Кто-то изменяет сборку A, увеличивая значение константы до 2, и восстанавливает A (но не B)
В этом случае сборка B в скомпилированном виде все равно будет сравнивать значение от 1 до 1, поскольку при компиляции B константа имела значение 1.
Фактически, если это единственное использование чего-либо из сборки A в сборке B, сборка B будет скомпилирована без зависимости от сборки A. Выполнение кода, содержащего это выражение в сборке B, не будет загружать сборку A.
Таким образом, константы должны использоваться только для вещей, которые никогда не изменятся. Если это значение может измениться или когда-нибудь изменится в будущем, и вы не можете гарантировать, что все другие сборки будут перестроены одновременно, поле только для чтения больше подходит, чем константа.
Так что все в порядке:
- public const Int32 NumberOfDaysInAWeekInGregorianCalendar = 7;
- public const Int32 NumberOfHoursInADayOnEarth = 24;
пока это не так:
- public const Int32 AgeOfProgrammer = 25;
- public const String NameOfLastProgrammerThatModifiedAssembly = "Джо Программист";
Редактировать 27 мая 2016 г.
ОК, только что получил ответ, поэтому я перечитал свой ответ здесь, и это на самом деле немного неправильно.
Теперь, намерение спецификации языка C # - это все, что я написал выше. Вы не должны использовать что-то, что не может быть представлено литералом как const
.
Но ты можешь? Ну да ....
Давайте посмотрим на тип decimal
.
public class Test
{
public const decimal Value = 10.123M;
}
Давайте посмотрим, как выглядит этот класс на самом деле , если рассматривать его с помощью 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 )
Позвольте мне разобрать это для вас:
.field public static initonly
соответствует:
public static readonly
Правильно, const decimal
на самом деле readonly decimal
.
Реальная сделка заключается в том, что компилятор будет использовать это DecimalConstantAttribute
, чтобы творить чудеса.
Теперь, это единственная магия, которую я знаю о компиляторе C #, но я подумал, что стоит упомянуть.