Почему нет статических структур?

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

Джеффри Рихтер объясняет это достаточно просто «CLR всегда разрешает создание размерных типов. Статичная структура будет противоречить этому ограничению». Если вам всё понятно, дальше можете не читать, если нет, продолжим.

В С++ тип пользовательского типа данных (класс или структура)1 влияет исключительно на видимость по-умолчанию полей и методов. В языке C# тип определяет область выделения памяти оператором new. Экземпляры классов (ссылочные типы) всегда создаются в куче, экземпляры структур (размерные типы) всегда создаются на стэке.

Возможность выделять память под объекты пользовательских типов в автоматической памяти (стэк, регистр) является одним из ключевых отличий C# от Java. В Java память для всех объектов пользовательских типов выделяются в куче. Впрочем, это зависит от реализации компилятора. Например, размещение локальных переменных, являющихся экземплярами классов, на стэке, в Java компиляторе компании Excelsior позволило повысить скорость работы оптимизированного таким образом кода в 40 (сорок) раз. Записано со-слов разработчиков :-)

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

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

Следовательно, если определение типа, в нашем случае это структура, указывает компилятору сгенерировать код по созданию объекта, у компилятора всегда должна быть возможность «сделать это» т.е. сгенерировать код по созданию объекта размерного типа.

В терминах С++ создание «нулевого указателя на структуру» в С# невозможно. По той же причине нельзя осуществить приведение к типу с помощью оператора as.

int i = value as int;	

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

namespace Dotnetnomicon
{
  public class SlotRef
  {
    private int _x;
    private int _data;
  }
	
  public struct SlotVal
  {
    private int _x;
    private int _data;
  }
	    
  private static void TestMemoryManagement()
  {
    int v1;
    int v2 = 840;
    SlotRef objClass = new SlotRef();
    SlotVal objStruct = new SlotVal();
  }
}   
	
//Декомпилированный код функции TestMemoryManagement

.maxstack 1
.locals init (
  [0] int32 
  [1] int32
  [2] class Dotnetnomicon.SlotRef 
  [3] valuetype Dotnetnomicon.SlotVal 
)

IL_0000: nop
//Помещает переданное значение с типом int32 в стек вычислений как int32.
IL_0001: ldc.i4 840

//Извлекает верхнее значение в стеке вычислений и сохраняет его в списке локальных 
//переменных с индексом 1.
IL_0006: stloc.1

//Создает новый объект или новый экземпляр типа значения и помещает ссылку на объект
//в стек вычислений.
IL_0007: newobj instance void Dotnetnomicon.SlotRef::.ctor()

//Извлекает верхнее значение в стеке вычислений и сохраняет его в списке локальных 
//переменных с индексом 2.
IL_000c: stloc.2
	
//Загружает в стек вычислений адрес локальной переменной с указанным индексом
IL_000d: ldloca.s 3

//Инициализирует каждое поле типа значения с определенным адресом пустой ссылкой 
//или значением 0 соответствующего простого типа.
IL_000f: initobj valuetype Dotnetnomicon.SlotVal
	
IL_0015: ret


  1. Не путать с объединением (union).