Глава.14 .Символы,.строки.и.обработка.текста
Первый из них,
Intern
, ищет
String
во внутренней хеш-таблице. Если строка
обнаруживается, возвращается ссылка на соответствующий объект
String
. Иначе
создается копия строки, она добавляется во внутреннюю хеш-таблицу, и возвраща-
ется ссылка на копию. Если приложение больше не удерживает ссылку на исход-
ный объект
String
, уборщик мусора вправе освободить память, занимаемую этой
строкой. Обратите внимание, что уборщик мусора не вправе освободить строки, на
которые ссылается внутренняя хеш-таблица, поскольку в ней самой есть ссылки
на эти
String
. Объекты
String
, на которые ссылается внутренняя хеш-таблица,
нельзя освободить, пока не выгружен соответствующий домен приложения или
не закрыт поток.
Как и
Intern
, метод
IsInterned
получает параметр
String
и ищет его во
внутренней хеш-таблице. Если поиск удачен,
IsInterned
возвращает ссылку на
интернированную строку. В противном случае он возвращает
null
, а саму строку
не вставляет в хеш-таблицу.
По умолчанию при загрузке сборки CLR интернирует все литеральные строки,
описанные в метаданных сборки. Выяснилось, что это отрицательно сказывается на
производительности из-за необходимости дополнительного поиска в хеш-таблицах,
поэтому Microsoft теперь позволяет отключить эту «функцию». Если сборка от-
мечена атрибутом
System.Runtime.CompilerServices.CompilationRelaxations
Attribute
, определяющим значение флага
System.Runtime.CompilerServices.
CompilationRelaxations.NoStringInterning
, то в соответствии со спецификацией
ECMA среда CLR
может
отказаться от интернирования строк, определенных в ме-
таданных сборки. Обратите внимание, что в целях повышения производительности
работы приложения компилятор C# всегда при компиляции сборки определяет
этот атрибут/флаг.
Даже если в сборке определен этот атрибут/флаг, CLR может предпочесть
интернировать строки, но на это не стоит рассчитывать. Никогда не стоит писать
код, рассчитанный на интернирование строк, если только вы сами в своем коде
явно не вызываете метод
Intern
типа
String
. Следующий код демонстрирует
интернирование строк:
String s1 = "Hello";
String s2 = "Hello";
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // Должно быть 'False'
s1 = String.Intern(s1);
s2 = String.Intern(s2);
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // 'True'
При первом вызове метода
ReferenceEquals
переменная
s1
ссылается на объ-
ект-строку
"Hello"
в куче, а
s2
— на другую объект-строку
"Hello"
. Поскольку
ссылки разные, выводится значение
False
. Однако если выполнить этот код в CLR
версии 4.5, будет выведено значение
True
. Дело в том, что эта версия CLR игно-
рирует атрибут/флаг, созданный компилятором C#, и интернирует литеральную
строку
"Hello"
при загрузке сборки в домен приложений. Это означает, что
s1
и
s2
371
Тип.System String
ссылаются на одну строку в куче. Однако, как уже отмечалось, никогда не стоит
писать код с расчетом на такое поведение, потому что в последующих версиях этот
атрибут/флаг может приниматься во внимание, и строка
"Hello"
интернироваться
не будет. В действительности, CLR версии 4.5 учитывает этот атрибут/флаг, но
только если код сборки создан с помощью утилиты
NGen exe
.
Перед вторым вызовом метода
ReferenceEquals
строка
"Hello"
явно интерни-
руется, в результате
s1
ссылается на интернированную строку
"Hello"
. Затем при
повторном вызове
Intern
переменной
s2
присваивается ссылка на ту же самую стро-
ку
"Hello"
, на которую ссылается
s1
. Теперь при втором вызове
ReferenceEquals
мы гарантировано получаем результат
True
независимо от того, была ли сборка
скомпилирована с этим атрибутом/флагом.
Теперь на примере посмотрим, как использовать интернирование строки для по-
вышения производительности и снижения нагрузки на память. Показанный далее
метод
NumTimesWordAppearsEquals
принимает два аргумента: слово и массив строк,
в котором каждый элемент массива ссылается на одно слово. Метод определяет,
сколько раз указанное слово содержится в списке слов, и возвращает число:
private static Int32 NumTimesWordAppearsEquals(String word, String[]
wordlist) {
Int32 count = 0;
for (Int32 wordnum = 0; wordnum < wordlist.Length; wordnum++) {
if (word.Equals(wordlist[wordnum], StringComparison.Ordinal))
count++;
}
return count;
}
Как видите, этот метод вызывает метод
Equals
типа
String
, который сравнивает
отдельные символы строк и проверяет, все ли символы совпадают. Это сравнение
может выполняться медленно. Кроме того, массив
wordlist
может иметь много
элементов, которые ссылаются на многие объекты
String
, содержащие тот же на-
бор символов. Это означает, что в куче может существовать множество идентичных
строк, которые не должны уничтожаться в процессе уборки мусора.
А теперь посмотрим на версию этого метода, которая написана с интернирова-
нием строк:
private static Int32 NumTimesWordAppearsIntern(String word, String[]
wordlist) {
// В этом методе предполагается, что все элементы в wordlist
// ссылаются на интернированные строки
word = String.Intern(word);
Int32 count = 0;
for (Int32 wordnum = 0; wordnum < wordlist.Length; wordnum++) {
if (Object.ReferenceEquals(word, wordlist[wordnum]))
count++;
}
return count;
}
372
Do'stlaringiz bilan baham: |