В C #, почему String является ссылочным типом, который ведет себя как тип значения?

Строка является ссылочным типом, даже если она имеет большинство характеристик типа значений, таких как неизменяемость и перегрузка == для сравнения текста, а не обеспечение того, чтобы они ссылались на один и тот же объект.

Почему тогда не строка только типа значения?

Строки не являются типами значений, поскольку они могут быть огромными и должны храниться в куче. Типы значений (во всех реализациях CLR еще) хранятся в стеке. Stack allocating strings будет разбивать всевозможные вещи: стек составляет всего 1 Мбайт для 32-битных и 4 МБ для 64-битных, вам нужно будет вставлять каждую строку, влекущую за собой штраф за копирование, вы не можете ставить строки и использовать память будет воздушный шар и т. д. …

(Изменить: добавлено уточнение о том, что хранилище типов значений является деталью реализации, что приводит к такой ситуации, когда у нас есть тип с ценностной семантикой, не наследующей от System.ValueType. Спасибо, Бен.)

Это не тип значения, потому что производительность (пространство и время!) Была бы ужасной, если бы это был тип значения, и его значение пришлось копировать каждый раз, когда оно было передано и возвращено из методов и т. Д.

Он имеет ценностную семантику, чтобы поддерживать мир в здравом уме. Можете ли вы представить себе, насколько сложно было бы кодировать, если

 string s = "hello"; string t = "hello"; bool b = (s == t); 

установить b как false ? Представьте себе, насколько сложным будет кодирование любого приложения.

Различие между ссылочными типами и типами значений в основном представляет собой компромисс между производительностью в дизайне языка. У ссылочных типов есть некоторые накладные расходы на строительство, уничтожение и garbage collection, поскольку они создаются в куче. С другой стороны, типы значений имеют накладные расходы на вызовы методов (если размер данных больше, чем указатель), поскольку весь объект копируется, а не только указатель. Поскольку строки могут быть (и обычно) намного большими, чем размер указателя, они создаются как ссылочные типы. Кроме того, как указал Servy, размер значения типа должен быть известен во время компиляции, что не всегда относится к строкам.

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

Типы ссылок обычно изменяемы, но могут быть разработаны как неизменные, если это имеет смысл. Строки определяются как неизменные, потому что это делает возможной оптимизацию. Например, если один и тот же строковый литерал встречается несколько раз в одной и той же программе (что довольно часто), компилятор может повторно использовать один и тот же объект.

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

Говоря в более общем плане, у строк есть то, что называется семантикой значений . Это более общая концепция, чем типы значений, которая представляет собой детальную реализацию реализации C #. Типы значений имеют семантику значений, но ссылочные типы могут также иметь семантику значений. Когда тип имеет семантику значения, вы не можете сказать, является ли базовая реализация ссылочным типом или типом значений, поэтому вы можете рассмотреть эту деталь реализации.

Не только строки являются неизменяемыми ссылочными типами. Многолистные delegates тоже. Вот почему безопасно писать

 protected void OnMyEventHandler() { delegate handler = this.MyEventHandler; if (null != handler) { handler(this, new EventArgs()); } } 

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

 string s1 = "my string"; //some code here string s2 = "my string"; 

Скорее всего, оба экземпляра константы «моя строка» будут выделены в вашей сборке только один раз.

Если вы хотите управлять строками, как обычный ссылочный тип, поместите строку внутри нового StringBuilder (строка s). Или используйте MemoryStreams.

Если вы хотите создать библиотеку, где вы ожидаете, что в ваших функциях будут переданы огромные строки, либо определите параметр как StringBuilder, либо как Stream.

Это поздний ответ на старый вопрос, но во всех остальных ответах отсутствует точка, которая заключается в том, что .NET не имел генериков до .NET 2.0 в 2005 году.

String является ссылочным типом, а не типом значения, поскольку для Microsoft крайне важно гарантировать, что строки могут быть сохранены наиболее эффективным способом в не общих коллекциях , таких как System.Collection.ArrayList .

Для хранения значения типа в неэквивалентной коллекции требуется специальное преобразование в object типа, который называется бокс. Когда CLR вводит тип значения, он переносит значение внутри System.Object и сохраняет его в управляемой куче.

Для чтения значения из коллекции требуется обратная операция, которая называется распаковкой.

Как бокс, так и unboxing имеют незначительную стоимость: бокс требует дополнительного распределения, unboxing требует проверки типов.

В некоторых ответах неверно утверждается, что string никогда не могла быть реализована как тип значения, поскольку ее размер является переменной. На самом деле легко реализовать строку как структуру данных фиксированной длины, используя страtagsю оптимизации небольших строк: строки будут храниться в памяти непосредственно в виде последовательности символов Юникода, за исключением больших строк, которые будут храниться как указатель на внешний буфер. Оба представления могут быть рассчитаны на одну и ту же фиксированную длину, то есть размер указателя.

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

Кроме того, реализуются строки (разные для каждой платформы) и когда вы начинаете сшивать их вместе. Как с помощью StringBuilder . Он выделяет буфер для копирования, когда вы достигаете цели, он выделяет для вас еще больше памяти, в надежде, что если вы сделаете большую производительность конкатенации, это не будет затруднено.

Может, Джон Скит может помочь здесь?

Это в основном проблема производительности.

Наличие строк ведет к тому, что тип значения LIKE помогает при написании кода, но с его BE тип значения будет иметь огромный успех.

Для углубленного просмотра загляните в красивую статью о строках в рамках .net.

Как вы можете указать, что string является ссылочным типом? Я не уверен, что это важно, как это реализовано. Строки в C # неизменны точно, так что вам не нужно беспокоиться об этой проблеме.

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

Строки неизменяемы по очень веской причине, это не имеет никакого отношения к тому, что это ссылочный тип, но имеет много общего с управлением памятью. Простое создание нового объекта при изменении размера строки, чем перемещение объектов в управляемой куче. Я думаю, что вы смешиваете ценности / ссылочные типы и концепции неизменных объектов.

Насколько «==»: Как вы сказали, «==» – это перегрузка оператора, и снова он был реализован по очень веской причине, чтобы сделать фрейм более полезным при работе со строками.

Не так просто, как строки, состоящие из массивов символов. Я рассматриваю строки как массивы символов []. Поэтому они в куче, потому что место ссылки памяти хранится в стеке и указывает на начало ячейки памяти массива в куче. Размер строки неизвестен до того, как он будет выделен … идеально подходит для кучи.

Вот почему строка действительно неизменяема, потому что, когда вы меняете ее, даже если она имеет тот же размер, компилятор не знает этого и должен выделять новый массив и назначать символы позициям в массиве. Это имеет смысл, если вы думаете о строках, как о том, как языки защищают вас от необходимости выделять память «на лету» (читая C как программирование)

В очень простых словах любое значение, имеющее определенный размер, может рассматриваться как тип значения.

Рискуя получить еще один таинственный проголосовавший голос … факт, что многие упоминают стек и память в отношении типов значений и примитивных типов, состоит в том, что они должны вписываться в регистр в микропроцессоре. Вы не можете нажимать или выталкивать что-то в / из стека, если требуется больше бит, чем регистр … инструкции, например, «pop eax» – потому что eax имеет 32 бита в 32-битной системе.

Типы примитивов с плавающей запятой обрабатываются FPU, который имеет ширину 80 бит.

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

  • Как я могу напечатать значения wchar_t для консоли?
  • Заглавие первого слова предложения в строке с несколькими предложениями
  • Как я могу поместить строку в Java?
  • Преобразование строки Java в дату
  • Форматирование CSV с разделителями-запятыми, чтобы заставить Excel интерпретировать значение как строку
  • Как преобразовать String в int в Java?
  • Метод обратного эффекта java String.split ()?
  • Подсчитайте количество всех слов в строке
  • Разделить строку строки фрейма данных на несколько столбцов
  • Вычисление частоты каждого слова в предложении в java
  • stringstream, string и char * ошибка конверсии
  • Interesting Posts

    Есть ли API Google Voice?

    UAC отключается один раз в день в Windows 7

    Как использовать OpenFileDialog для выбора папки?

    Как загрузить java-class (а не сервлет), когда сервер tomcat запускает

    Какой устанавливаемый компонент предоставляет «запускающие группы» в Eclipse?

    Безопасность беспроводной мыши и клавиатуры

    LINQ to Entities не распознает метод «Int32 Parse (System.String)», и этот метод не может быть переведен в выражение хранилища

    Как получить доступ к личной информации Facebook, используя идентификатор ASP.NET (OWIN)?

    Интернационализация с угловым 4

    Как определить кодировку файлов в OSX?

    Селектор CSS (идентификатор содержит часть текста)

    Совместное использование локальной сети Windows XP: заблокирован 1 ГБ .zip-файл; Видя «Отказано в доступе» на копии?

    Могут ли переопределенные методы различаться по типу возврата?

    Передайте пользовательский объект в ASP.NET Webmethod из jQuery, используя JSON

    У встроенных типов есть стандартные конструкторы?

    Давайте будем гением компьютера.