Класс StringBuilder для работы со строками в C#

Как известно, в C# строки относятся к неизменяемым типам данных, то есть, когда мы производим над переменной типа string какие-либо операции, то в памяти создается новая строка, затем в новую строку копируются символы из старой строки и только потом старая строка удаляется из памяти. Таким образом, производить большое количество изменений строки становится слишком накладным, как в плане использования памяти, так и производительности приложения, в принципе. Однако, используя класс C# StringBuilder мы можем избежать описанных выше проблем.

Класс StringBuilder

StringBuilder — это класс для работы со строками. Расположен этот класс в пространстве имен System.Text. В отличие от String, класс StringBuilder представляет изменяемую последовательность символов и, следовательно, позволяющий более рационально использовать память.

У класса StringBuilder определены следующие методы:

Append Добавляет строковое представление указанного значения к экземпляру StringBuilder. Используя этот метод, можно без явных преобразований добавлять к строке числа, массивы символов, логические значения, объекты и т.д.
AppendFormat Добавляет к экземпляру StringBuilder строку, возвращаемую в результате форматирования исходной строки.
AppendJoin(Char, Object[]) Сцепляет строковые представления элементов из указанного массива объектов, помещая между ними заданный символьный разделитель, а затем добавляет результат к текущему экземпляру StringBuilder. Имеется ряд перегруженных версий этого метода, позволяющих предоставлять в качестве второго параметра ряд массивов других типов значений.
AppendLine Добавляет символ переноса строки по умолчанию в конец текущего объекта StringBuilder.
Clear Удаляет все символы из текущего экземпляра StringBuilder.
CopyTo Копирует символы из указанного сегмента этого экземпляра StringBuilder в указанный массив Char.
Insert Вставляет строковое представление указанного типа в StringBuilder в указанную позицию.
Remove Удаляет указанный диапазон символов из данного экземпляра.
Replace Замещает все вхождения указанного символа в данном экземпляре на другой указанный знак.
ToString Преобразует значение данного экземпляра в String.

 

Рассмотрим пример использования StringBuilder для работы со строками:

using System.Text;
namespace SBuilder
{
    internal class Program
    {
        static void Main(string[] args)
        {
            StringBuilder stringBuilder= new StringBuilder();//создали экземпляр
            stringBuilder.AppendLine("Hello world"); //добавили строку
            stringBuilder.AppendLine("Этот текст будет на второй строке"); //добавили вторую строку
            //далее будет одна строка
            stringBuilder.Append("Сегодня: ");
            stringBuilder.AppendFormat("{0} время {1}", DateOnly.FromDateTime(DateTime.Now), TimeOnly.FromDateTime(DateTime.Now));
            Console.WriteLine(stringBuilder.ToString());    
        }
    }
}

Результат вывода в консоли будет следующий:

Hello world
Этот текст будет на второй строке
Сегодня: 19.11.2022 время 16:22

Здесь мы, используя StringBuilder создали строку и вывели её в консоль. При этом, в примере явно отсутствует ответ на вопрос — в чем наглядное преимущество использования этого класса по сравнению с обычным string? Чтобы ответить на этот вопрос, измерим время выполнения большого количества операций объединения строк, используя string и StringBuilder.

Производительность StringBuilder

Так как на время выполнения той или иной операции оказывает влияние в том числе и окружение в котором происходит тестирование, то указанные ниже результаты можно считать ориентировочными.

Метод Append

Засекать время операции будем от начала создания объекта и до окончания операций объединения строк:

       static void Main(string[] args)
        {
            int Stringlength = 10; //итоговая длина строки
            int repeatCount = 100;//количество повторений создания строки

            Stopwatch stopwatch = new();
            
            long total = 0;

            for (int k = 0; k < repeatCount; k++)
            {
                stopwatch.Restart();
                string str = "";
                for (int i = 0; i < Stringlength; i++)
                {
                    str += "0";
                }
                stopwatch.Stop();
                total += stopwatch.Elapsed.Ticks;
            }
            Console.WriteLine($"Среднее время {total/ repeatCount}");

            total= 0;
           

            for (int k = 0; k < repeatCount; k++)
            {
                stopwatch.Restart();
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < Stringlength; i++)
                {
                    builder.Append("0");
                }
                stopwatch.Stop();
                total += stopwatch.Elapsed.Ticks;
            }
            Console.WriteLine($"Среднее время {total / repeatCount}");


        }
    }
}

Результаты

Длина строки string StringBuilder
10 4 2
100 86 11
1000 2420 51
10000 172250 697
100000 16507314 5861

Метод Insert

Здесь будем вставлять один символ в начало уже созданной ранее строки

Длина строки string StringBuilder
10 5 5
100 87 39
1000 2426 1401
10000 175578 123306
100000 16321564 14974280

Как можно видеть из результатов, StringBuilder значительно ускоряет работу с конкатенацией строк, хотя при выполнении операции вставки подстроки в строку выигрыш и не столь очевидный. Но, опять же оговорюсь — многое в тестах зависит и от окружения в котором эти тесты выполнялись и, возможно, что у вас результаты будут выглядеть несколько иначе.

Свойство Capacity

Длина строки в StringBuilder, как и у string хранится в свойстве Length. При этом, у StringBuilder имеется ещё одно полезное свойство — Capacity, которое возвращает или задает максимальное число знаков, которое может содержаться в памяти. По умолчанию, при каждой операции в StringBuilder происходит выделение памяти под строку, что несколько замедляет работу. Если же нам изначально известна конечная длина строки, которую мы должны получить, то создав экземпляр StringBuilder с заранее заданным свойством Capacity мы можем повысить производительность. Чтобы задать значение Capacity при создании экземпляра StringBuilder необходимо передать значение в конструктор:

Capacity = 1000;
StringBuilder builder = new StringBuilder(Capacity);

Например, создание строки длиной 10 000 символов методом Append без использования значения свойства Capacity в конструкторе у меня заняло время равное 6440 тактам, а при использовании — 5677.

Когда использовать StringBuilder

В вопросе типа «string vs String Builder» следует придерживаться рекомендаций разработчиков из Microsoft, а именно StringBuilder рекомендуется использовать в следующих случаях:

  1. Если предполагается, что код будет вносить неизвестное количество изменений в строку во время разработки (например, при использовании цикла для сцепления случайного числа строк, содержащих данные, введенные пользователем).
  2. Если предполагается, что код вносит значительное количество изменений в строку.

Также, можно отметить, что StringBuilder не содержит методов поиска подстрок в строке, поэтому, при выборе между string и StringBuilder стоит обратить внимание и на то, предполагается ли производить в строке большое количество операций поиска. Если да, то, возможно, стоит отказаться от StringBuilder или же производить необходимые операции поиска перед добавлением строки в StringBuilder.

Итого

Класс StringBuilder предоставляет методы для работы со строками в C#. При этом, StringBuilder может значительно повысить производительность приложения, если предполагается использование большого количества операций объединения строк. Наличие большого количества перегруженных методов у StringBuilder позволяет без дополнительных преобразований производить конкатенацию строк, используя различные типы данных.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии