Podstawowym powodem / problemem jest to, że projektanci specyfikacji CLS (która definiuje sposób interakcji języków z .net) nie zdefiniowali sposobu, w jaki członkowie klasy mogliby określić, że muszą być wywoływani bezpośrednio, a nie za pośrednictwemcallvirt
, bez wykonującego kontrola zerowego odniesienia; nie dostarczył też wielu definicji struktur, które nie podlegałyby „normalnemu” boksu.
Gdyby specyfikacja CLS zdefiniowała takie środki, to .net byłby w stanie konsekwentnie podążać za wyprzedzeniem ustalonym przez Common Object Model (COM), zgodnie z którym odwołanie do ciągu zerowego traktowano semantycznie jako równoważne pustemu ciągowi, a dla innych zdefiniowane przez użytkownika niezmienne typy klas, które powinny mieć semantykę wartości, aby podobnie definiować wartości domyślne. Zasadniczo to, co by się stało, byłoby dla każdego członka String
, np. Length
Napisane jako coś w rodzaju [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }
. Takie podejście zapewniłoby bardzo dobrą semantykę dla rzeczy, które powinny zachowywać się jak wartości, ale z powodu problemów z implementacją muszą być przechowywane na stercie. Największą trudnością przy takim podejściu jest to, że semantyka konwersji między takimi typami Object
może być nieco mętna.
Alternatywnym podejściem byłoby zezwolenie na definicję specjalnych typów struktur, które nie dziedziczyły, Object
ale zamiast tego miały niestandardowe operacje boksu i rozpakowania (które konwertowałyby na / z jakiegoś innego typu klasy). Przy takim podejściu istniałby typ klasy, NullableString
który zachowuje się jak ciąg znaków, oraz niestandardowy typ struktury String
, który przechowywałby pojedyncze prywatne pole Value
typu String
. Próba konwersji String
na NullableString
lub Object
zwróci, Value
jeśli nie jest zerowa lub String.Empty
jeśli jest zerowa. Próba String
użycia rzutowania na odwołanie inne niż null do NullableString
instancji spowoduje zapisanie odwołania w Value
(być może przechowywanie wartości null, jeśli długość wynosi zero); rzutowanie dowolnego innego odwołania spowodowałoby wyjątek.
Chociaż ciągi muszą być przechowywane na stercie, nie ma koncepcyjnego powodu, dla którego nie powinny zachowywać się jak typy wartości o wartości domyślnej innej niż null. Przechowywanie ich jako „normalnej” struktury, która zawierała odniesienie, byłoby skuteczne w przypadku kodu, który używał ich jako „łańcucha” typu, ale dodałoby dodatkową warstwę pośredniczości i nieefektywności podczas rzutowania na „obiekt”. Chociaż nie przewiduję dodania żadnej z powyższych funkcji w późniejszym terminie, być może projektanci przyszłych ram mogą rozważyć ich włączenie.