Строки и неизменность

В мире программирования строка - это массив символов System.Char, которые вместе представляют текст. В языке программирования C # вы можете объявить строку и распечатать ее значение следующим образом:

string str = "Hello World";
Console.WriteLine(str);
//This will print out "Hello World" to the console.

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

Почему строки неизменяемы

В C # CLR (Common Language Runtime) отвечает за определение места для хранения строк. В последнем разделе я заметил, что строка - это массив символов. CLR реализует массив для хранения строк. Массивы представляют собой структуру данных фиксированного размера, что означает, что их размер не может быть динамически увеличен или уменьшен. После того, как массиву назначен размер, его нельзя изменить. Чтобы увеличить массив, данные должны быть скопированы и клонированы в новый массив, который CLR помещает в новый блок памяти. Если вы редактируете строку, вы действительно не изменяете эту строку; скорее, среда CLR создает новую ссылку на память для измененной строки, а исходная строка будет удалена из памяти посредством сборки мусора.

Давайте заглянем под капот

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

Давайте подробнее рассмотрим пример из предыдущего раздела. Мы создадим новую строку, затем изменим ее и объясним, что происходит в памяти во время всего этого.

string str = "Hello World";
Console.WriteLine(str);
//This will print out "Hello World" to the console.

В программе мы создаем строковый объект с именем str и присваиваем ему значение «Hello World». Однако в памяти CLR создает блоки пространства для хранения этой переменной. Для простоты предположим, что среда CLR использует ячейку памяти 1000 для хранения str. Поскольку это объект, среда CLR сохранит его в куче, а не в стеке. Теперь давайте изменим эту строку.

str += " edited";
Console.WriteLine(str);
//This will print out "Hello World edited" to the console.

Когда вы запустите этот код, вы увидите «Hello World edited». Поскольку эта строка неизменяема, среда CLR снова создает новые блоки пространства в памяти для хранения этой переменной. CLR назначит новую ячейку памяти, скажем, 1500 для этой новой переменной. В конце концов, сборщик мусора избавится от исходной строки, хранящейся в ячейке 1000, и очистит ее из памяти.

За и против

Как и почти все остальное, есть причины использовать неизменяемые строки и есть причины не использовать неизменяемые строки. Почему вам следует использовать неизменяемые строки? Одно из преимуществ состоит в том, что они потокобезопасны. Если вы работаете с многопоточной системой, не будет риска тупиковой ситуации или каких-либо проблем с параллелизмом, поскольку при изменении строки вы на самом деле просто создаете новый объект в памяти. Еще одно преимущество состоит в том, что вам не придется беспокоиться о том, что вы их случайно измените. Вам не нужно принимать дополнительные меры безопасности (например, защитную копию объекта), которые могут потребоваться с изменяемым объектом.

Почему неизменяемые строки - плохая идея? Основная проблема заключается в том, что постоянно меняющиеся строки могут привести к проблемам с производительностью. Мы объясним это в блоке кода. Если вы вернетесь к фрагментам кода из предыдущего раздела, вы увидите, что мы изменили строку только один раз. Предположим, у нас есть такой сценарий:

string str = "Hello World";
Console.WriteLine(str);
//This will print out "Hello World" to the console.

for (int i = 0; i < 10; i++)
{
	str += " again";
	Console.WriteLine(str);
}

Этот код печатает «снова» для каждой итерации (десять раз) после исходного «Hello World». Однако для каждой итерации, поскольку строка неизменяема, в памяти происходит то, что CLR десять раз выделяет новое пространство в памяти и сохраняет новую переменную str и каждый раз создает блоки большего размера для сохранения большего количества данных.