Два популярных класса, которые вы будете часто использовать при работе со строками в .NET Core, – это классы String и StringBuilder. Вы должны знать о передовых методах использования обоих этих классов для создания приложений, которые минимизируют выделение ресурсов и обладают высокой производительностью. В этой статье обсуждаются лучшие практики, которым мы можем следовать при работе со строками в C #.

Для работы с примерами кода, приведенными в этой статье, в вашей системе должна быть установлена ​​Visual Studio 2019. Если у вас еще нет копии, вы можете скачать Visual Studio 2019 здесь. Обратите внимание, что мы также будем использовать BenchmarkDotNet для отслеживания производительности методов. Если вы не знакомы с BenchmarkDotNet, я предлагаю прочитать эта статья первый.

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

Создание проекта консольного приложения .NET Core в Visual Studio

Прежде всего, давайте создадим проект консольного приложения .NET Core в Visual Studio. Предполагая, что в вашей системе установлена ​​Visual Studio 2019, выполните действия, описанные ниже, чтобы создать новый проект консольного приложения .NET Core.

  1. Запустите интегрированную среду разработки Visual Studio.
  2. Нажмите «Создать новый проект».
  3. В окне «Создать новый проект» выберите «Консольное приложение (.NET Core)» из отображаемого списка шаблонов.
  4. Нажмите “Далее.
  5. В окне «Настроить новый проект» укажите имя и расположение для нового проекта.
  6. Щелкните “Создать”.

Мы будем использовать этот проект для работы с классами String и StringBuilder в следующих разделах этой статьи.

Установите пакет BenchmarkDotNet NuGet

Для работы с BenchmarkDotNet необходимо установить пакет BenchmarkDotNet. Вы можете сделать это либо с помощью диспетчера пакетов NuGet внутри интегрированной среды разработки Visual Studio 2019, либо выполнив следующую команду в консоли диспетчера пакетов NuGet:

Install-Package BenchmarkDotNet

Объединение строк с использованием String и StringBuilder в C #

Неизменяемый объект – это объект, который нельзя изменить после того, как он был создан. Поскольку строка является неизменяемым типом данных в C #, при объединении двух или более строк создается новая строка.

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

Когда вы хотите изменить строку, Common Language Runtime генерирует новую строку с нуля и отбрасывает старую. Итак, если вы добавите к строке ряд символов, вы повторно создадите одну и ту же строку в памяти несколько раз. Напротив, класс StringBuilder выделяет память для буферного пространства, а затем записывает новые символы непосредственно в буфер. Распределение происходит только один раз.

Рассмотрим следующие два метода:

[Benchmark]
public string StringConcatenationUsingStringBuilder()
        {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < NumberOfRuns; i++)
            {
                stringBuilder.AppendLine("Hello World" + i);
            }
            return stringBuilder.ToString();
        }
[Benchmark]
public string StringConcatenationUsingString()
        {
            string str = null;
            for (int i = 0; i < NumberOfRuns; i++)
            {
                str += "Hello World" + i;
            }
            return str;
        }

На рисунке 1 показаны тесты производительности этих двух методов.

строка против конструктора строк 01 IDG

Фигура 1.

Как видно на рисунке 1, объединение строк с помощью StringBuilder происходит намного быстрее, потребляет гораздо меньше памяти и использует меньше сборок мусора во всех поколениях по сравнению с использованием оператора «+» для объединения двух или более строк.

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

Короче говоря, используйте StringBuilder только для большого количества конкатенаций.

Уменьшение выделения StringBuilder с помощью многоразового пула в C #

Рассмотрим следующие два метода: один создает экземпляры StringBuilder без использования пула, а другой создает экземпляры StringBuilder с использованием повторно используемого пула. Используя повторно используемый пул, вы можете уменьшить выделение. Если вам нужен экземпляр StringBuilder, вы можете получить его из пула. Когда вы закончите использовать экземпляр StringBuilder, вы можете вернуть его обратно в пул.

[Benchmark]
    public void CreateStringBuilderWithoutPool()
        {
            for (int i = 0; i < NumberOfRuns; i++)
            {
                var stringBuilder = new StringBuilder();
                stringBuilder.Append("Hello World" + i);
            }
        }
[Benchmark]
    public void CreateStringBuilderWithPool()
        {
            var stringBuilderPool = new
            DefaultObjectPoolProvider().CreateStringBuilderPool();
            for (var i = 0; i < NumberOfRuns; i++)
            {
                var stringBuilder = stringBuilderPool.Get();
                stringBuilder.Append("Hello World" + i);
                stringBuilderPool.Return(stringBuilder);
            }
        }

На рисунке 2 показаны тесты производительности этих двух методов.

строка против конструктора строк 02 IDG

Фигура 2.

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

Извлечение строк с использованием подстроки против добавления в C #

Давайте теперь сравним производительность метода Substring класса String и метода Append класса StringBuilder для извлечения строки из другой строки.

Рассмотрим следующий фрагмент кода, который иллюстрирует два метода: один извлекает строку из другой строки с помощью метода Substring класса String, а другой делает то же самое с помощью метода Append класса StringBuilder.

[Benchmark]
    public string ExtractStringUsingSubstring()
        {
            const string str = "This is a sample text";
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < NumberOfRuns; i++)
            {
                stringBuilder.Append(str.Substring(0, 10));
            }
            return stringBuilder.ToString();
        }
[Benchmark]
    public string ExtractStringUsingAppend()
        {
            const string str = "This is a sample text";
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < NumberOfRuns; i++)
            {
                stringBuilder.Append(str, 0, 10);
            }
            return stringBuilder.ToString();
        }

На рисунке 3 показаны тесты производительности этих двух методов.

строка против конструктора строк 03 IDG

Рисунок 3.

Как видно на рисунке 3, использование метода Append класса StringBuilder для извлечения строки происходит быстрее и потребляет меньше ресурсов, чем использование Substring.

Соединение строк с помощью String.Join vs. StringBuilder.AppendJoin в C #

При объединении строк используйте StringBuilder.AppendJoin вместо String.Join для уменьшения выделения. Рассмотрим следующий фрагмент кода, который иллюстрирует два метода – один, который объединяет строки с помощью String.Join, а другой, который делает то же самое с помощью StringBuilder.AppendJoin.

[Benchmark]
    public string JoinStringsUsingStringJoin()
        {
            var stringBuilder = new StringBuilder();
            for (int i = 0; i < NumberOfRuns; i++)
            {
                stringBuilder.Append(string.Join("Hello", ' ', "World"));
            }
            return stringBuilder.ToString();
        }
[Benchmark]
    public string JoinStringsUsingAppendJoin()
        {
            var stringBuilder = new StringBuilder();
            for (int i = 0; i < NumberOfRuns; i++)
            {
                stringBuilder.AppendJoin("Hello", ' ', "World");
            }
            return stringBuilder.ToString();
        }

На рисунке 4 показаны тесты производительности этих двух методов.

строка против конструктора строк 04 IDG

Рисунок 4.

Как и при извлечении строки из строки, StringBuilder предлагает преимущества по сравнению с классом String при соединении строк. Как вы можете видеть на рисунке 4, использование AppendJoin класса StringBuilder снова быстрее и более эффективно с точки зрения ресурсов, чем использование Join класса String.

При использовании StringBuilder вы также можете установить емкость экземпляра StringBuilder для повышения производительности. Если вы знаете размер создаваемой строки, вы можете установить начальную емкость при создании экземпляра StringBuilder. Это может значительно уменьшить выделение памяти.

В заключение отметим, что метод String.Create – это еще один способ улучшить производительность обработки строк в .NET Core. Он обеспечивает эффективный способ создания строк во время выполнения. Я расскажу о String.Create в одном из следующих постов.

Авторские права © 2021 IDG Communications, Inc.


#Когда #использовать #String #или #StringBuilder #NET #Core

Source link