Почему строка является ссылочным типом? - PullRequest
7 голосов
/ 07 сентября 2010

Почему строка является ссылочным типом, хотя обычно это примитивный тип данных, например int, float или double.

Ответы [ 3 ]

18 голосов
/ 07 сентября 2010

В дополнение к причинам, указанным Дэном:

Типы значений: по определению это те типы, которые хранят свои значения в себе, а не , ссылающиеся наценность где-то еще.Вот почему типы значений называются «типами значений», а ссылочные типы называются «ссылочными типами».Таким образом, ваш вопрос на самом деле «почему строка ссылается на ее содержимое, а не просто , содержащую ее содержимое?»

Это потому, что типы значений имеют свойство nice, которое every экземпляр данного типа значения имеет тот же размер в памяти.

И что?Почему это хорошая собственность?Хорошо, предположим, что строки были типами значений, которые могут иметь любой размер, и рассмотрим следующее:

string[] mystrings = new string[3];

Каково начальное содержимое этого массива из трех строк?Для типов значений «null» нет, поэтому единственное разумное, что нужно сделать, - это создать массив из трех пустых строк.Как это будет в памяти?Подумайте об этом немного.Как бы вы это сделали?

Теперь предположим, что вы говорите

string[] mystrings = new string[3];
mystrings[1] = "hello";

Теперь у нас есть "", "привет" и "" в массиве. Куда в памяти уходит «привет»? Каков вообще размер слота, который был выделен для mystrings [1]?Память для массива и его элементов должна уходить куда-то .

Это оставляет CLR со следующими вариантами:

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

Команда CLR выбрала последний вариант. Превращение строк в ссылочные типы означает, что вы можете эффективно создавать их массивы.

10 голосов
/ 07 сентября 2010

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

Новый ответ

Обновление : Вот в чем дело.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[], то, пока тип массива является ссылочным типом, содержимое строки будет фактически , а не передаваться по значению - только ссылка на массив будетбыть.Я все еще думаю, что это в основном правильный ответ.

1 голос
/ 07 сентября 2010

String является ссылочным типом, а не типом значения. Во многих случаях вы знаете длину строки и содержимое строки, в таких случаях легко выделить память для строки. но рассмотрим что-то вроде этого.

string s = Console.ReadLine();

невозможно узнать подробности выделения для "s" во время компиляции. Пользователь вводит значения, и вся введенная строка / строка сохраняется в s. Итак, строки хранятся в куче, так что память перераспределяется в соответствии с содержимым строки s. И ссылка на эту строку хранится в стеке.

Чтобы узнать больше, прочитайте: .net zero by petzold

Чтение: сборка мусора из CLR через C # для подробностей выделения в стек.

Редактировать: Console.WriteLine (); в Console.ReadLine ();

...