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


Метод System.String.Intern

Замечание

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

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

Интерн-пул можно использовать средой выполнения для экономии строкового хранилища. Однако автоматическое интернирование строковых литералов не гарантируется — в зависимости от того, как была скомпилирована и выполнена сборка, некоторые литералы могут не добавляться в пул.

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

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 перечисления указывает, что сборка не требует интернирования строковых литералов. По умолчанию компилятор C# выдает CompilationRelaxationsAttributeNoStringInterning флаг для каждой сборки для повышения производительности, что означает, что строковые литералы не гарантированно добавляются в интернированный пул. Вы можете настроить NoStringInterning в сборке с помощью атрибута CompilationRelaxationsAttribute.

При публикации приложения с использованием нативного AOT отключение NoStringInterning не поддерживается. При использовании нативного AOT строковые литералы не гарантированно добавляются в интернальный пул строк, поэтому Intern может не найти совпадение для строки, которая является литералом в исходном коде.