Основная причина / проблема заключается в том, что разработчики спецификации CLS (которая определяет, как языки взаимодействуют с .net) не определили средство, с помощью которого члены класса могли бы указывать, что они должны вызываться напрямую, а не через callvirt
, без того, чтобы вызывающая сторона выполняла проверка нулевой ссылки; и при этом это не обеспечило множество определяющих структур, которые не будут подвергаться «нормальному» боксу.
Если бы спецификация CLS определила такое средство, тогда .net мог бы последовательно следовать примеру, установленному Общей объектной моделью (COM), при которой ссылка на нулевую строку считалась семантически эквивалентной пустой строке, а для других Определяемые пользователем типы неизменяемых классов, которые должны иметь семантику значений, чтобы также определять значения по умолчанию. По сути, то, что должно было бы произойти для каждого члена String
, например, Length
было бы записано как-то так [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }
. Этот подход предложил бы очень хорошую семантику для вещей, которые должны вести себя как значения, но из-за проблем с реализацией их нужно хранить в куче. Самая большая трудность при таком подходе заключается в том, что семантика преобразования между такими типами Object
может стать немного мутной.
Альтернативный подход состоял бы в том, чтобы разрешить определение специальных структурных типов, которые не наследуются, Object
а вместо этого имеют пользовательские операции упаковки и распаковки (которые преобразуются в / из некоторого другого типа класса). При таком подходе будет существовать тип класса, NullableString
который ведет себя так же, как и строка, и специальный тип структуры String
, который будет содержать одно закрытое поле Value
типа String
. Попытка преобразовать String
в NullableString
или Object
вернет, Value
если не ноль, или String.Empty
если ноль. При попытке привести к String
ненулевой ссылке на NullableString
экземпляр будет сохранена ссылка Value
(возможно, при сохранении нуля, если длина равна нулю); приведение любой другой ссылки вызовет исключение.
Несмотря на то, что строки должны храниться в куче, концептуально нет причин, по которым они не должны вести себя как типы значений, которые имеют ненулевое значение по умолчанию. Хранение их в виде «нормальной» структуры, содержащей ссылку, было бы эффективно для кода, который использовал их как тип «строка», но добавил бы дополнительный уровень косвенности и неэффективности при приведении к «объекту». Хотя я не предполагаю, что .net добавит любую из перечисленных выше функций в более позднюю дату, возможно, разработчики будущих фреймворков могут рассмотреть возможность их включения.