StringBuilder
Чек нельзя исправить, а маркерную доску можно. На ней дописывают слово, стирают букву, вписывают новую, и доска всё время одна и та же. Если текст нужно долго собирать по кусочку, удобнее писать на доске, а печатать чек один раз, когда всё готово.
Посмотрим, что происходит, когда строку собирают в цикле через +=:
string line = "";
for (int i = 1; i <= 5; i++)
{
line += i;
}
Console.WriteLine(line); // 12345Результат верный, но давайте посчитаем работу. Строку изменить нельзя, поэтому каждый += создаёт новую строку и копирует в неё весь накопленный текст:
- Первый проход создаёт
"1". - Второй создаёт
"12": копирует1и дописывает2. - Третий создаёт
"123"и копирует уже два символа. - Четвёртый создаёт
"1234"и копирует три символа. - Пятый копирует четыре символа ради одного нового.
За пять проходов программа создала пять строк длиной от 1 до 5 символов и записала в них 1 + 2 + 3 + 4 + 5 = 15 символов. Если кусков 10 000 по одному символу, += запишет в новые строки 1 + 2 + … + 10 000, то есть 50 005 000 символов. Работа растёт как квадрат числа кусков.
StringBuilder
StringBuilder — та самая доска: он хранит изменяемый текст в буфере, участке памяти, куда можно дописывать. Метод Append дописывает кусок в конец, не создавая новую строку, а ToString() в конце один раз создаёт готовую строку:
using System.Text;
var sb = new StringBuilder();
for (int i = 1; i <= 5; i++)
{
sb.Append(i);
}
string line = sb.ToString();
Console.WriteLine(line); // 12345
Console.WriteLine(sb.Length); // 5Давайте разберём пример по строкам:
using System.Text;подключает пространство имён, в котором живётStringBuilder. Об этом подробнее ниже.new StringBuilder()создаёт пустую доску.varздесь уместен: тип виден справа.sb.Append(i)дописывает в конец текст числаi. Старые символы остаются на месте, и копировать их не нужно.sb.ToString()создаёт из доски обычную строку"12345".sb.Length— сколько символов сейчас на доске.
Для 10 000 кусков StringBuilder запишет каждый символ один раз, а в ToString() скопирует все символы в строку ещё раз. Выходит около 20 000 записанных символов вместо 50 с лишним миллионов.
Пространство имён и директива using
Типы .NET разложены по группам с именами. Такая группа называется пространством имён: Console лежит в пространстве System, а StringBuilder — в System.Text. Программа .NET 10 сама подключает System и ещё несколько пространств, а System.Text в их число не входит.
Строка using System.Text; в самом начале файла — это директива using: она подключает пространство имён к файлу. После неё StringBuilder можно писать коротким именем.
Доску можно править
Доска позволяет не только дописывать. StringBuilder умеет ещё четыре вещи:
Insertвставляет текст в заданную позицию, например в начало;Replaceзаменяет кусок прямо на доске, без новой строки;sb[4] = 'C'меняет один символ, а у строки так нельзя;Clear()стирает всё, иLengthстановится0.
using System.Text;
var sb = new StringBuilder("cat");
sb.Append('s');
sb.Insert(0, "two ");
sb[4] = 'C';
Console.WriteLine(sb.ToString()); // two Cats
sb.Replace("two", "three");
Console.WriteLine(sb.ToString()); // three Cats
sb.Clear();
Console.WriteLine(sb.Length); // 0Давайте разберём по строкам:
new StringBuilder("cat")создаёт доску, на которой уже написаноcat.Append('s')дописываетs: на доскеcats.Insert(0, "two ")вставляет текст с индекса0:two cats.sb[4] = 'C'меняет символ с индексом4, первую буквуcats:two Cats.Replace("two", "three")меняетtwoнаthree:three Cats.Clear()стирает всё, иLengthстановится0.
Здесь доска и StringBuilder расходятся. Доска одного размера: когда место на ней кончается, писать дальше некуда. StringBuilder в этом случае сам добавляет ещё кусок памяти, а написанное остаётся на месте.
Когда нужен StringBuilder
Этот вопрос часто задают на собеседовании: зачем нужен StringBuilder, если есть +? Ответ из двух частей:
- строка неизменяема, поэтому каждый
+=в цикле создаёт новую строку и копирует в неё весь накопленный текст; StringBuilderдописывает куски в один изменяемый буфер и создаёт строку один раз, вToString().
Для двух-трёх кусков StringBuilder не нужен. Компилятор превращает выражение a + b + c в один вызов, который создаёт одну строку, а интерполяция читается ещё лучше.
Частая ошибка
Забыли подключить пространство имён:
var sb = new StringBuilder(); // ✗
sb.Append("Hi");
Console.WriteLine(sb.ToString());error CS0246: The type or namespace name 'StringBuilder' could not be found (are you missing a using directive or an assembly reference?)Программа не компилируется: компилятор ищет StringBuilder в подключённых пространствах имён и не находит. В скобках сообщение само подсказывает причину: возможно, пропущена директива using. Допишите первой строкой файла using System.Text;.
Главное
- Каждый
+=со строкой создаёт новую строку и копирует весь текст, поэтому работа цикла с+=растёт как квадрат числа кусков. StringBuilderхранит изменяемый текст:Appendдописывает,Insert,Replaceиsb[i]правят на месте, аToString()создаёт строку.StringBuilderлежит в пространстве имёнSystem.Text, и файл начинают сusing System.Text;.- Для нескольких кусков хватает
+или интерполяции, аStringBuilderберут для сборки в цикле.