Да, этот ответ был принят, а затем я изменил его.Вероятно, я должен включить исходный ответ внизу, так как это то, что было принято ОП.
Новый ответ
Обновление : Вот в чем дело.string
абсолютно должен вести себя как ссылочный тип.До сих пор причины этого были затронуты во всех ответах: тип string
не имеет постоянного размера, нет смысла копировать все содержимое строки из одного метода в другой, иначе массивы string[]
нужно изменить размер тем - просто назвать несколько.
Но вы все равно можете определить string
как struct
, который внутренне указывает на массив char[]
или даже char*
указатель и int
для его длины, делают его неизменным, и вуаля! , у вас будет тип, который ведет себя как ссылочный тип, но технически тип значения.
Это было бы довольно глупо, если честно.Как указал Эрик Липперт в нескольких комментариях к другим ответам, определение типа значения, подобного этому, в основном совпадает с определением ссылочного типа.Почти во всех смыслах он был бы неотличим от ссылочного типа, определенного таким же образом.
Таким образом, ответ на вопрос «Почему string
является ссылочным типом?»в основном: «Делать это типом значения было бы просто глупо».Но если это причина only , то на самом деле логический вывод состоит в том, что string
действительно можно было бы определить как struct
, как описано выше, и не было бы особенно хороших аргументов против этого выбора.
Однако есть причин, по которым лучше сделать string
class
, чем struct
, которые являются более чем чисто интеллектуальными.Вот пара, о которой я мог подумать:
Чтобы предотвратить бокс
Если string
был типом значения, то каждый раз, когда вы передавали его какому-либо методу, ожидающему object
должен быть в штучной упаковке, что создаст новый object
, который будет раздувать кучу и вызывать бессмысленное давление ГХ.Поскольку строки в основном везде , их постоянное появление в боксе было бы большой проблемой.
Для интуитивного сравнения на равенство
Да, string
может переопределить Equals
независимо от того, является ли это ссылочным типом или типом значения.Но если бы это был тип значения, то ReferenceEquals("a", "a")
вернул бы false ! Это связано с тем, что оба аргумента были бы упакованы, а аргументы никогда не имеют равных ссылок(насколько я знаю).
Итак, хотя это правда, что вы можете определить тип значения для действия точно так же, как ссылочный тип, если он состоит из одного поля ссылочного типа, это все равно не будет точно то же самое.Поэтому я поддерживаю это как более полную причину, по которой string
является ссылочным типом: вы можете сделать его типом значения, но это только обременит его ненужными недостатками.
Оригинальный ответ
Это ссылочный тип, потому что передаются только ссылки .
Если бы это был тип значения, то каждый раз, когда вы передавали строку из одного метода в другой, вся строкабыть скопированным *.
Поскольку это ссылочный тип, а не строковые значения, такие как "Hello world!"передается вокруг - "Привет, мир!"Между прочим, это 12 символов, что означает (по крайней мере) 24 байта памяти - только ссылки на эти строки передаются.Передача ссылки намного дешевле, чем передача каждого символа в строке.
Кроме того, это действительно , а не обычный тип данных.Кто тебе это сказал?
* На самом деле, это не совсем так.Если строка содержит внутренний массив char[]
, то, пока тип массива является ссылочным типом, содержимое строки будет фактически , а не передаваться по значению - только ссылка на массив будетбыть.Я все еще думаю, что это в основном правильный ответ.