Метод System.String.Intern

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

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

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

Метод Intern использует интерновый пул для поиска строки, равной значению str. Если такая строка существует, возвращается ссылка в интернированного пула. Если строка не существует, ссылка str добавляется в интернирующий пул, возвращается эта ссылка.

В следующем примере строка 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.

Сравните этот метод с методом IsInterned .

В следующем примере переменная назначается ссылку, и переменная str1str2 назначается ссылкеString.EmptyString.Empty, возвращаемой путем вызова Intern метода после преобразования StringBuilder объекта, значение которого равно String.Empty строке. Затем ссылки, содержащиеся str1 и str2 сравниваются для равенства. str1 и str2 равны.

string str1 = String.Empty;
string str2 = String.Empty;

StringBuilder sb = new StringBuilder().Append(String.Empty);
str2 = String.Intern(sb.ToString());

if ((object)str1 == (object)str2)
    Console.WriteLine("The strings are equal.");
else
    Console.WriteLine("The strings are not equal.");
let str1 = String.Empty
let str2 = String.Empty

let sb = StringBuilder().Append String.Empty
let str3 = String.Intern(string sb)

if (str1 :> obj) = (str3 :> obj) then
    printfn "The strings are equal."
else
    printfn "The strings are not equal."
Dim str1 As String = String.Empty
Dim str2 As String = String.Empty

Dim sb As StringBuilder = New StringBuilder().Append(String.Empty)
str2 = String.Intern(sb.ToString())

If CObj(str1) Is CObj(str2) Then
    Console.WriteLine("The strings are equal.")
Else
    Console.WriteLine("The strings are not equal.")
End If

Замечания, связанные с быстродействием

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

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