Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Теоретический_курс.doc
Скачиваний:
36
Добавлен:
10.11.2019
Размер:
7.68 Mб
Скачать

4. Постоянство строк

Как ни странно, содержимое объекта типа string не подлежит изменению. Этo означает, что однажды созданную последовательность символов изменить нельзя. Но данное ограничение способствует более эффективной реализации символьных строк. Поэтому этот, на первый взгляд, очевидный недостаток на самом деле превращается в преимущество. Так, если требуется строка в качестве разновидности уже имеющейся строки, то для этой цели следует создать новую строку, содержащую все необходимые изменения. А поскольку неиспользуемые строковые объекты автоматически собираются в «мусор», то о дальнейшей судьбе ненужных строк можно даже не беспокоиться.

Следует, однако, подчеркнуть, что переменные ссылки на строки (т.е. объекты типа string) подлежат изменению, а следовательно, они могут ссылаться на другой объект. Но содержимое самого объекта типа string не меняется после его создания.

Рассмотрим пример:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace LC_Console

{

class Program

{

static void addNewString()

{

string s = "Начальная символьная строка";

s = "Новая символьная строка";

Console.WriteLine(s);

}

static void Main()

{

addNewString();

Console.WriteLine("\nДля продолжения нажмите любую клавишу . . ."); ;

Console.ReadKey();

}

}

}

Скомпилируем приложение и загрузим результирующую сборку в утилиту IL DASM («Дизассемблер IL», файл ildasm.exe). Утилиту можно найти по умолчанию по следующему пути (для Windows 7): Пуск -> Все программы -> Microsoft Visual Studio 2010 -> Microsoft Windows SDK Tools -> Дизассемблер IL. Главное окно утилиты выглядит так:

Рис. 4. 1. Окно утилиты IL DASM (ildasm.exe)

Открываем EXE-файл скомпилированного приложения из предыдущего кода консольного приложения. На рисунке показан CIL-код, который будет сгенерирован для метода addNewString:

Рис. 4. 2. IL DASM: разбор ассемблерных действий метода addNewString

Обратим внимание на наличие многочисленных вызовов кода операции ldstr (загрузка строки). Этот код операции ldstr в CIL предусматривает выполнение загрузки нового объекта string в управляемую кучу. В результате предыдущий объект, в котором содержалось значение «"This is my first stroke"», будет в конечном итоге удалено сборщиком мусора.

3.8.6. Класс «StringBuilder» Класс «StringBuilder»

Когда строка в C# конструируется классом String, выделяется ровно столько памяти, сколько необходимо для хранения данной строки. Однако, в пространстве имён System.Text имеется класс StringBuilder, который поступает по-другому и обычно выделяет больше памяти, чем нужно в данный момент. У разработчика приложения, есть возможность указать, сколько именно памяти должен выделить StringBuilder, но если этого не сделать, то будет выбран объём по умолчанию, который зависит от размера начального текста, инициализирующего экземпляр StringBuilder. Класс StringBuilder имеет два главных свойства:

  • Length, показывающее длину строки, содержащуюся в объекте в данный момент

  • Capacity, указывающее максимальную длину строки, которая может поместиться в выделенную для объекта память.

Любые модификации строки происходят внутри блока памяти, выделенного экземпляру StringBuilder. Это делает добавление подстрок и замену индивидуальных символов строки очень эффективными. Удаление или вставка подстрок неизбежно остаются менее эффективными, потому что при этих операциях приходится перемещать в памяти части строки. Выделять новую память и, возможно, полностью перемещать ее содержимое приходится только при выполнении ряда действий, которые приводят к превышению выделенной емкости строки. В дополнение к избыточной памяти, выделяемой изначально на основе экспериментов, StringBuilder имеет свойство удваивать свою ёмкость, когда происходит переполнение, а новое значение емкости не установлено явно.

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

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace LC_Console

{

class Program

{

static void Main()

{

StringBuilder strBuild = new StringBuilder("Это начальный текст в строке", 120);

strBuild.AppendFormat(". Этот текст добавлен при вызове метода AppendFormat");

Console.WriteLine(strBuild);

Console.WriteLine("Для продолжения нажмите любую клавишу . . ."); ;

Console.ReadKey();

}

}

}

В данном примере начальная ёмкость StringBuilder равна 120. Всегда лучше сразу указывать емкость, превышающую предполагаемую длину строки, чтобы объекту StringBuilder не приходилось заново выделять память при переполнении. По умолчанию устанавливается ёмкость в 16 символов. Схематически данный пример работает следующим образом:

Рис. 1. 1. Использование памяти экземпляром класса StringBuilder strBuild из кода выше

Т.е. при вызове метода AppendFormat остаток текста помещается в пустое пространство без необходимости перераспределения памяти. Однако реальная эффективность, которую несёт с собой применение StringBuilder, проявляется при выполнении повторных подстановок текста.

В следующей таблице перечислены основные методы класса StringBuilder:

Метод

Назначение

Append()

Добавляет строку к текущей строке

AppendFormat()

Добавляет строку, сформированную в соответствии со спецификатором формата

Insert()

Вставляет подстроку в строку

Remove()

Удаляет символ из текущей строки

Replace()

Заменяет все вхождения символа другим символом или вхождения подстроки другой подстрокой

ToString()

Возвращает текущую строку в воде объекта System.String (переопределение метода класса System.Object)

Рассмотрим практический пример использования класса StringBuilder:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace LC_Console

{

class Program

{

static void Main()

{

StringBuilder strBuild = new StringBuilder("Это начальный текст в строке", 120);

strBuild.AppendFormat(". Этот текст добавлен при вызове метода AppendFormat");

Console.WriteLine(strBuild);

// Зашифруем строку, хранящуюся в переменной strBuild

Console.WriteLine("Исходная строка: \n {0}\n", strBuild);

for (int i = 'я'; i >= 'a'; i--)

strBuild = strBuild.Replace((char)i, (char)(i + 3));

Console.WriteLine("Зашифрованная строка:\n {0}", strBuild);

Console.WriteLine("\nДля продолжения нажмите любую клавишу . . ."); ;

Console.ReadKey();

}

}

}

Рис. 1. 2. Результат работы кода выше