Различие между ссылочными типами и типами значений в основном является компромиссом производительности при разработке языка. У ссылочных типов есть некоторые накладные расходы на создание, уничтожение и сборку мусора, поскольку они создаются в куче. С другой стороны, типы значений накладываются на вызовы методов (если размер данных больше, чем указатель), потому что весь объект копируется, а не только указатель. Поскольку строки могут быть (и обычно имеют размер) намного больше, чем размер указателя, они разработаны как ссылочные типы. Кроме того, как указал Servy, размер типа значения должен быть известен во время компиляции, что не всегда имеет место для строк.
Вопрос изменчивости является отдельной проблемой. И ссылочные типы, и типы значений могут быть изменяемыми или неизменяемыми. Типы значений обычно являются неизменяемыми, поскольку семантика для изменяемых типов значений может сбивать с толку.
Ссылочные типы, как правило, являются изменяемыми, но могут быть разработаны как неизменяемые, если это имеет смысл. Строки определены как неизменяемые, потому что это делает возможной определенную оптимизацию. Например, если один и тот же строковый литерал встречается несколько раз в одной и той же программе (что довольно часто встречается), компилятор может повторно использовать один и тот же объект.
Так почему же "==" перегружено для сравнения строк по тексту? Потому что это самая полезная семантика. Если две строки равны по тексту, они могут или не могут быть одной и той же ссылкой на объект из-за оптимизации. Таким образом, сравнение ссылок довольно бесполезно, а сравнение текста - почти всегда то, что вы хотите.
Говоря более широко, Strings имеет то, что называется семантикой значения . Это более общая концепция, чем типы значений, что является специфической реализацией C #. Типы значений имеют семантику значений, но ссылочные типы также могут иметь семантику значений. Когда тип имеет семантику значений, вы не можете точно сказать, является ли базовая реализация ссылочным типом или типом значения, поэтому вы можете считать это реализацией.
is
тесты в стороне), ответ, вероятно, «по историческим причинам». Производительность копирования не может быть причиной, поскольку нет необходимости физически копировать неизменяемые объекты. Теперь это невозможно изменить, не нарушая код, который фактически используетis
проверки (или подобные ограничения).std::string
поведение, похожее на коллекцию, является старой ошибкой, которую сейчас нельзя исправить.