Для чего нужна иммутабельность строк в C# и какие преимущества она дает
Что проверяют
Понимаете ли вы, что неизменяемость string это осознанный компромисс: безопасность и простота ценой лишних аллокаций. Следом почти всегда спрашивают про StringBuilder, пул строк и склейку строк без мусора.
Как отвечать
System.String неизменяем: Replace, ToUpper, Substring, Trim не трогают исходный объект, а возвращают новый. Что это даёт:
- передачу ссылок без копий: строку можно отдать в метод или сохранить в поле, её никто не поменяет у вас за спиной;
- потокобезопасность объекта: одну строку читают из разных потоков без блокировок;
- стабильные ключи: хеш строки не меняется, поэтому она надёжный ключ в
DictionaryиHashSet; - защиту проверок: путь к файлу после валидации не подменить между проверкой и использованием;
- интернирование: одинаковые литералы CLR хранит в одном экземпляре, и это безопасно только потому, что их нельзя изменить.
Цена в аллокациях: s += x в цикле каждый раз копирует всю строку, это квадратичная сложность и нагрузка на GC. Что использовать:
StringBuilder: склейка в цикле или неизвестного числа частей;string.Joinиstring.Concat: готовая коллекция;ReadOnlySpan<char>: срезы черезAsSpan()и парсинг без новых строк;string.CreateиDefaultInterpolatedStringHandler: второй появился в C# 10 для интерполяции, оба нужны там, где важна каждая аллокация.
Где спотыкаются
Вызывают name.Trim(); без присваивания и удивляются, что пробелы остались. Метод вернул новую строку, её просто выбросили.
Говорят, что все строки попадают в пул. Автоматически интернируются только литералы и константы, строки из ввода, файлов и конкатенации создаются заново, пока не вызвать string.Intern.
Путают потокобезопасность объекта и переменной. Строка неизменна, но поле, куда разные потоки пишут новые ссылки, требует синхронизации. Отдельная ловушка с паролями: строку нельзя затереть в памяти, а SecureString Microsoft не рекомендует для нового кода.