Почему C # ограничивает набор типов, которые могут быть объявлены как const ?

StackOverflow https://stackoverflow.com/questions/441420

  •  22-07-2019
  •  | 
  •  

Вопрос

Ошибка компилятора CS0283 указывает, что только базовые типы POD (а также строки, перечисления и нулевые ссылки) могут быть объявлены как const.Есть ли у кого-нибудь теория относительно обоснования этого ограничения?Например, было бы неплохо иметь возможность объявлять значения const других типов, таких как IntPtr .

Я считаю, что концепция const на самом деле это синтаксический сахар в C #, и что он просто заменяет любое использование имени литеральным значением.Например, учитывая следующее объявление, любая ссылка на Foo будет заменена на "foo" во время компиляции.

const string Foo = "foo";

Это исключило бы любые изменяемые типы, поэтому, возможно, они выбрали это ограничение вместо того, чтобы определять во время компиляции, является ли данный тип изменяемым?

Это было полезно?

Решение

Из самого Спецификация 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-оператор и его блок будут удалены.
  • Поскольку значение константы компилируется в код, а не ссылка на константу, использование констант из других сборок никаким образом не приведет к автоматическому обновлению скомпилированного кода, если значение константы должно измениться (чего не должно быть!)

Другими словами, при следующем сценарии:

  1. Сборка A, содержит константу с именем "Version", имеющую значение 1
  2. Сборка B, содержит выражение, которое анализирует номер версии сборки A на основе этой константы и сравнивает его с 1, чтобы убедиться, что оно может работать со сборкой
  3. Кто-то изменяет сборку 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 Возраст программиста = 25;
  • общедоступная постоянная строка nameoflastprogrammer, которая модифицировала сборку = "Программист Джо";

Редактировать 27 мая 2016

Хорошо, только что получил положительный результат, поэтому я перечитал свой ответ здесь, и на самом деле это немного неверно.

Теперь, в намерение из спецификации языка C # вытекает все, что я написал выше.Вы не должны использовать что-то, что не может быть представлено с помощью литерала в виде const.

Но сможешь ли ты?Ну, в общем, да....

Давайте взглянем на decimal Тип.

public class Test
{
    public const decimal Value = 10.123M;
}

Давайте посмотрим, как выглядит этот класс в самом деле когда на тебя смотрят с пренебрежением:

.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 #, но я подумал, что об этом стоит упомянуть.

Другие советы

  

Есть ли у кого-нибудь теория об обосновании этого ограничения?

Если это разрешено быть просто теорией, моя теория состоит в том, что значения констант примитивных типов могут быть выражены в параметрах буквального кода операции в MSIL ... но значения других, не примитивных типов не могут, потому что MSIL не ' Синтаксис для выражения значения пользовательского типа в виде литерала.

  

Я считаю, что понятие const на самом деле является синтаксическим сахаром в C #, и оно просто заменяет любое использование имени буквальным значением

Что делает компилятор с объектами const на других языках?

Вы можете использовать readonly для изменяемых типов, которые могут быть оценены во время выполнения. См. эту статью о различиях.

Констант

ограничен числами и строками в C #, потому что компилятор заменяет переменную литеральным значением в MSIL. Другими словами, когда вы пишете:

const string myName = "Bruce Wayne";
if (someVar == myName)
{
   ...
}

фактически рассматривается как

if (someVar == "Bruce Wayne")
{
   ...
}

и да, компилятор C # достаточно умен, чтобы рассматривать оператор равенства (==) в строках как

string1.Equals(string2)

Мне кажется, что только типы значений могут быть выражены как константа (за исключением строк, которые находятся где-то между значением и типом объекта).

Это нормально для меня: объекты (ссылки) должны быть размещены в куче, но константы не выделяются вообще (так как они заменяются во время компиляции).

Короче говоря, все простые типы, перечисления и строки являются неизменяемыми, но, например, Struct - нет. Вы можете иметь структуру с изменяемым состоянием (поля, свойства, даже ссылки на ссылочные типы). Поэтому компилятор не может убедиться (во время компиляции), что внутреннее состояние переменной Struct не может быть изменено. Поэтому компилятор должен быть уверен, что тип по определению неизменен для использования в константном выражении.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top