Поделиться через


Метод System.String.Intern

В этой статье приводятся дополнительные замечания к справочной документации по этому API.

Среда CLR экономит хранилище строк, сохраняя таблицу, называемую интернальным пулом, которая содержит одну ссылку на каждую уникальную литеральную строку, объявленную или созданную программным способом в программе. Следовательно, экземпляр литеральной строки с определенным значением существует только один раз в системе. Например, если вы назначаете одну литеральную строку нескольким переменным, среда выполнения извлекает одну и ту же ссылку на литеральную строку из внутреннего пула и назначает ее каждой переменной.

Метод Intern использует интерновый пул для поиска строки, равной значению его параметра str. Если такая строка существует, возвращается ссылка из интернированного пула строк. Если строка не существует, ссылка str добавляется в интернирующий пул, и та же самая ссылка возвращается. (В отличие от этого, метод IsInterned(String) возвращает нулевую ссылку, если запрошенная строка не существует в интернированном пуле.)

В следующем примере строка s1, которая имеет значение MyTest, уже интернирована, так как это литерал в программе. Класс System.Text.StringBuilder создает новый строковый объект, имеющий то же значение, что s1и . Ссылка на эту строку присваивается s2. Метод Intern ищет строку, которая имеет то же значение, что s2. Так как такая строка существует, метод возвращает ту же ссылку, которая присвоена s1. Затем эта ссылка присваивается s3. Ссылки s1 и s2 не равны, так как они ссылаются на разные объекты; s1 и s3 равны, так как они ссылаются на одинаковую строку.

string s1 = "MyTest"; 
string s2 = new StringBuilder().Append("My").Append("Test").ToString(); 
string s3 = String.Intern(s2); 
Console.WriteLine((Object)s2==(Object)s1); // Different references.
Console.WriteLine((Object)s3==(Object)s1); // The same reference.
let s1 = "MyTest"
let s2 = StringBuilder().Append("My").Append("Test").ToString()
let s3 = String.Intern s2
printfn $"{s2 :> obj = s1 :> obj}" // Different references.
printfn $"{s3 :> obj = s1 :> obj}" // The same reference.
Dim s1 As String = "MyTest" 
Dim s2 As String = New StringBuilder().Append("My").Append("Test").ToString() 
Dim s3 As String = String.Intern(s2) 
Console.WriteLine(CObj(s2) Is CObj(s1))      ' Different references.
Console.WriteLine(CObj(s3) Is CObj(s1))      ' The same reference.

Вопросы, связанные с производительностью

Если вы пытаетесь уменьшить общий объем памяти, который выделяет ваше приложение, запомните, что при интернировании строки возникают два нежелательных побочных эффекта. Во-первых, память, выделенная для интернированных String объектов, скорее всего, не будет освобождена до тех пор, пока общеязыковая среда выполнения (CLR) не завершит работу. Причина заключается в том, что ссылка среды CLR на интернированный String объект может сохраняться после завершения приложения или даже домена приложения. Во-вторых, чтобы интернировать строку, необходимо сначала создать ее. Память, используемая объектом String, по-прежнему должна быть выделена, даже если она в конечном итоге будет передана на сборку мусора.

Член CompilationRelaxations.NoStringInterning перечисления указывает, что сборка не требует интернирования строковых литералов. Вы можете применить NoStringInterning к сборке с помощью атрибута CompilationRelaxationsAttribute . Кроме того, при использовании Ngen.exe (генератор собственных образов) для компиляции сборки заранее времени выполнения строки не интернируются между модулями.