Какой тип у строки в C#
Что проверяют
Не название типа, а понимание, как строка устроена: ссылочный тип, который ведёт себя почти как значимый. Следом обычно спрашивают, почему строку, переданную в метод, нельзя изменить снаружи, и когда нужен StringBuilder.
Как отвечать
string это псевдоним класса System.String. Ключевые свойства:
- ссылочный тип: объект лежит в куче, переменная хранит ссылку, значение по умолчанию у поля
null. Классsealed, наследоваться от него нельзя; - неизменяемость: любая «модификация» (
Replace,ToUpper,+) возвращает новый объект. Поэтому строки безопасно разделять между потоками и использовать как ключи словаря; - сравнение по значению:
==иEqualsдля строк перегружены и ординально сравнивают содержимое, а не ссылки; - UTF-16: строка хранит последовательность
charпо 2 байта. Эмодзи и другие символы вне базовой плоскости занимают дваchar, поэтомуLengthне равен числу видимых символов; - интернирование: одинаковые литералы попадают в пул и указывают на один объект. Строки, собранные в рантайме, интернируются только явно через
string.Intern.
Отсюда практические выводы. Конкатенация в цикле создаёт новую строку на каждой итерации и копирует всё накопленное, поэтому для сборки из множества кусков берут StringBuilder. При этом a + b + c в одном выражении компилятор сам превращает в один вызов string.Concat. Для разбора и нарезки без выделения памяти есть ReadOnlySpan<char> и AsSpan().
С C# 8 при включённых nullable reference types string и string? различаются для анализатора, но в рантайме это один тип.
Где спотыкаются
Называют строку значимым типом, потому что после передачи в метод исходная переменная не меняется. На деле метод просто присваивает своей копии ссылки новый объект.
Сравнивают без учёта регистра через a.ToLower() == b.ToLower(). Это лишние аллокации и сюрпризы с культурой, правильно string.Equals(a, b, StringComparison.OrdinalIgnoreCase).
Советуют StringBuilder для любой склейки двух строк, хотя выигрыш появляется только при многократном добавлении.