Они являются неизменяемыми типами значений в стеке. Что мешает мне иметь их const?
Литература:
Они являются неизменяемыми типами значений в стеке. Что мешает мне иметь их const?
Литература:
Потому что конструктор типа значения может что-то сделать - например, логика переключения в зависимости от времени суток. Постоянные типы значений имеют смысл интеллектуально, но на практике они просто не могут работать на пользовательских типах значений из-за гибкости конструкторов делать все, что им нравится. (Помните, что константы оцениваются во время компиляции, что означает, что ваш конструктор должен быть запущен во время компиляции.)
Const в С# означает, что он может быть определен во время компиляции, поэтому только очень примитивные типы, такие как int
и string
, могут быть константами.
Если вы находитесь на фоне C, ключевое слово readonly
может вам лучше подойти.
Я просто протестировал ключевое слово readonly
с помощью простой изменяемой структуры:
struct Test
{
public int value;
public void setInt(int val)
{
value = val;
}
}
static class Program
{
public static readonly Test t = new Test();
static void Main()
{
Console.WriteLine(t.value); // Outputs "0"
t.setInt(10);
//t.value = 10; //Illegal, will not let you assign field of a static struct
Console.WriteLine(t.value); // Still outputs "0"
}
}
Несмотря на то, что структура readonly
не является временной константой времени компиляции, среда выполнения не позволяет ее изменять. Даже шаг забросил метод setInt()
, он выглядит как изменение стоимости, но не показывает изменения в Main.
Я предполагаю, что сама структура помещается в "readonly" память, не разрешая им изменять. В отличие от класса, который просто сохраняет константу указателя, позволяя самим полям изменять их по своему усмотрению.
Итак, кажется, что static readonly
является эффектом a const
, даже для изменяемых структур.
Чтобы компилятор С# создавал значение типа const
для типа структуры, он должен знать, какие значения должны идти во всех своих полях. Компилятор С# по сути знает, как инициализировать поля определенных типов, например Decimal
, но для большинства типов значений он не имеет таких знаний.
Было бы возможно, чтобы компилятор предоставил средство объявления постоянных значений типа структуры в контекстах, где были открыты все поля struct
. Если поля структуры были private
, тогда константы этого типа могли быть объявлены только внутри структуры; если поля были internal
, константы могли быть объявлены в любом месте сборки; если public
, они могут быть объявлены где угодно.
Хотя я хотел бы видеть такую функцию, я не ожидаю, что какие-либо основные языки .net будут реализованы. Именованные константы типов, о которых по сути знает компилятор, могут участвовать в других константных выражениях, тогда как переменные static readonly
не могут. Если NumRows
- константа, равная 4, выражение, подобное Arr[3*NumRows+7]
, может быть заменено на Arr[19]
, даже если NumRows
определено во внешней сборке. Это дает такие константы существенное преимущество перед переменными static readonly
. Если, однако, константа имеет тип, который не распознает компилятор, он будет иметь очень ограниченную способность участвовать в любых постоянных выражениях, эффективно отрицая преимущество того, что он является константой в первую очередь. Если константа целочисленного значения имела открытое поле, компилятор мог бы использовать значение этого поля как константу, но поскольку создатели .net-языков философски противопоставлены структурам с открытыми полями, я бы не ожидал чтобы позволить такое использование, даже если бы они могли.
Были бы некоторые потенциальные варианты использования для поддержки констант над переменными static readonly
, но многие из таких случаев можно обрабатывать приемлемо с использованием существующих типов. Например, библиотека может вывести const Int64
для кодирования информации о своей версии и использовать это значение в качестве значения по умолчанию для метода GetLinkedVersionInfo
. Значение, о котором идет речь, будет "запекаться" в вызывающем коде, когда оно будет скомпилировано, что позволит методу сообщить, какую версию библиотеки, с которой был связан вызывающий, и, возможно, определить, есть ли проблемы совместимости с версией, с которой она работает.