Подтвердить что ты не робот

Почему структуры не могут быть объявлены как const?

Они являются неизменяемыми типами значений в стеке. Что мешает мне иметь их const?

Литература:

4b9b3361

Ответ 1

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

Ответ 2

Const в С# означает, что он может быть определен во время компиляции, поэтому только очень примитивные типы, такие как int и string, могут быть константами.

Если вы находитесь на фоне C, ключевое слово readonly может вам лучше подойти.

Ответ 3

Я просто протестировал ключевое слово 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, даже для изменяемых структур.

Ответ 4

Чтобы компилятор С# создавал значение типа 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. Значение, о котором идет речь, будет "запекаться" в вызывающем коде, когда оно будет скомпилировано, что позволит методу сообщить, какую версию библиотеки, с которой был связан вызывающий, и, возможно, определить, есть ли проблемы совместимости с версией, с которой она работает.