Как работает строка в С#?

Я знаю, что строки неизменяемы, после создания мы не можем их изменить, я читал, что если мы создаем новый строковый объект и присваиваем ему значение, а затем мы назначаем другое значение тому же самому строковому объекту внутри, фактически создается другой объект и присвоено новое значение. Допустим, у меня есть:

string str = "dog";            
str =  "cat";  

Если я напишу Console.WriteLine(str);, он вернет cat. Итак, внутри есть два объекта? Но у них одинаковое имя? Как это работает? Я провел некоторое исследование в Google, но пока не нашел чего-то достаточно убедительного для себя, чтобы прояснить свои мысли по этому поводу. Я знаю, что строки являются ссылочными типами, поэтому у нас есть объект в стеке со ссылкой на значение в куче, что происходит в этом случае? (см. код выше).

Я загрузил изображение, извините меня, если я ошибаюсь в отношении стека и кучи, поэтому я задаю этот вопрос. Отражает ли картинка то, что происходит в первой строке кода (string str = "dog";)? И что тогда должно произойти во второй строке кода?? Значение dog в куче меняется? И затем создается новый объект в стеке, ссылающийся на него? Что тогда происходит с объектом, который был там раньше? У них одинаковое имя? Извините за столько вопросов, но я думаю, что очень важно правильно это понять и знать, что происходит за кулисами...введите здесь описание изображения


person AlexGH    schedule 05.08.2016    source источник


Ответы (4)


просмотрите интернирование строк или .Net String Intern table или Внутренний пул CLR.
По сути, среда Common Language Runtime (CLR) поддерживает таблицу [уникальных] строковых значений, и всякий раз, когда вы манипулируете строкой в вашем коде CLR просматривает эту внутреннюю таблицу, чтобы увидеть, есть ли уже новое значение, которое вы пытаетесь создать, или нет. Если это так, он просто переназначает переменную, которую вы изменяете, чтобы она указывала на эту запись во внутреннем пуле. Если нет, он добавляет значение в пул и возвращает эту новую ссылку. Старые значения в пуле, на которые больше не ссылаются переменные, удаляются сборщиком мусора.

person Charles Bretana    schedule 05.08.2016
comment
мне очень трудно выбрать ответ, потому что все ваши ответы были очень описательными, теперь я прояснился по этому вопросу, спасибо!!! - person AlexGH; 05.08.2016
comment
Этот ответ просто неверен. Строки интернируются неявно только во время компиляции. Во время выполнения строки интернируются, только если код явно запрашивает их, вызывая метод string.Intern(). Кроме того, во внутреннем пуле string объектов никогда не удаляются сборщиком мусора. Они остаются там на неопределенный срок. Внутренний пул — это не кеш, это прежде всего место для размещения строковых литералов, найденных при компиляции кода. - person Peter Duniho; 28.02.2021

Когда вы присваиваете str «собаке», в памяти происходит то, что вы описали выше: ссылочная переменная str теперь «указывает» на местоположение строки, которую вы только что создали.

str => MEMORY LOCATION "1": "dog"
       MEMORY LOCATION "2":
       MEMORY LOCATION "3":

Когда str переназначается вашей новой строке, "кошке", она тоже создается в памяти, и теперь str корректируется так, чтобы она указывала на "кошку" в новом месте.

       MEMORY LOCATION "1": "dog"
str => MEMORY LOCATION "2": "cat"
       MEMORY LOCATION "3":

Что происходит с «собакой»? Теперь он фактически недоступен, поскольку у нас больше нет ссылки на его местоположение (в памяти, куче, термины в этой ситуации взаимозаменяемы). Позже, когда сборщик мусора проверит память на очистку, он поймет, что нет ничего, что ссылалось бы на «собаку», и пометит память для удаления и замены по мере необходимости.

person Jeremy Kato    schedule 05.08.2016

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

Для строки str = "cat"; в куче создается второй строковый объект, а переменная str изменяется так, чтобы она ссылалась на этот новый объект. Остается str, указывающий на "cat", и объект-сирота "dog" в куче без ссылок на него.

Объект "dog" может быть очищен сборщиком мусора, поскольку на него нет ссылок.

person itsme86    schedule 05.08.2016

Да, есть два объекта. Нет, у них не одно и то же имя. Старайтесь не думать о переменной как об «имени» самого объекта как такового — это больше похоже на временное имя местоположения объекта в памяти. (Причина, по которой неправильно думать о переменной как об «имени» объекта, заключается в том, что у вас может быть несколько переменных, ссылающихся на один и тот же объект; дело не в том, что у объекта есть несколько «имен» как таковых или что существует несколько объектов - именно так вы храните ссылку).

«string str» изначально содержит ссылку на строку «dog». После того, как вы присвоите «cat» «str», переменная теперь будет иметь ссылку на строку «cat».

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

Кстати, вы правы насчет значения в стеке со ссылкой на объект в куче - это хорошее различие.

person EJoshuaS - Reinstate Monica    schedule 05.08.2016