Почему строка, являющаяся ссылочным типом, должна быть непустым, тогда как другие ссылочные типы consts должны быть пустыми?

Насколько я знаю, string является ссылочным типом. const можно использовать с типом reference только в том случае, если они назначены null. Мой вопрос в том, что

почему string, являющемуся ссылочным типом, может быть назначена литеральная строка (или не нуль)?

Ответ 1

Из документа const:

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

Другими словами, это исключение. Жизнь была бы намного сложнее, если бы не было таких вещей, как строковые константы.

Вы также можете помнить, что это не все строки, например, вы не можете скомпилировать код const string test = new string('t', 7);, даже если бы вы могли с static string test = new string('t', 7);. С другой стороны, хотя вы можете определить строковую константу как строковый литерал (test = "value";), вы не можете определить другие ссылочные типы с литералом (Form f = ???).

Ответ 2

Это специальное правило на языке С# и функция CLR. Компилятор знает, как вставлять строки в метаданные сборки.

Ответ 3

Спецификация ECMA-334 С# имеет следующее примечание (выделение мое):

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

Ответ 4

Согласно MSDN:

Константы могут быть числами, булевыми значениями, строками или нулевой ссылкой.

Ответ 5

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

Такое поведение было бы возможно с любым типом класса, который не содержит никаких вложенных полей типа класса, но обычно может быть полезен только с неизменяемыми типами классов. Поскольку нет общих способов различать неизменяемые типы классов, особенно учитывая, что возможность того, что сборки могут быть загружены "необычными" определениями для таких типов, как, например, Tuple<int, int>, поведение ограничивается одним типом класса, который соответствует этому критерию и не может быть сделано иначе: System.String.