Второй вызов ReferenceEquals возвращает false. Почему строка в s4 не интернирована? (Меня не волнует преимущество StringBuilder перед конкатенацией строк.)
string s1 = "tom";
string s2 = "tom";
Console.Write(object.ReferenceEquals(s2, s1)); //true
string s3 = "tom";
string s4 = "to";
s4 += "m";
Console.Write(object.ReferenceEquals(s3, s4)); //false
Когда я делаю String.Intern(s4);
, я все равно получаю ложь.
Здесь и s3, и s4 интернированы, но их ссылки не равны?
string s3 = "tom";
string s4 = "to";
s4 += "m";
String.Intern(s4);
Console.WriteLine(s3 == s4); //true
Console.WriteLine(object.ReferenceEquals(s3, s4)); //false
Console.WriteLine(string.IsInterned(s3) != null); //true (s3 is interned)
Console.WriteLine(string.IsInterned(s4) != null); //true (s4 is interned)
Строка в s4
интернирован. Однако, когда вы выполняете s4 + = "m";
, вы создали новую строку, которая не будет интернирована, поскольку ее значение не является строковым литералом, а является результатом операции конкатенации строк. В результате s3
и s4
представляют собой два разных экземпляра строки в двух разных ячейках памяти.
Дополнительную информацию об интернировании строк можно найти в здесь , особенно в последнем примере. Когда вы выполняете String.Intern (s4)
, вы действительно интернируете строку, но все еще не выполняете проверку на равенство ссылок между этими двумя интернированными строками. Метод String.Intern
возвращает интернированную строку, поэтому вам нужно будет сделать следующее:
string s1 = "tom";
string s2 = "tom";
Console.Write(object.ReferenceEquals(s2, s1)); //true
string s3 = "tom";
string s4 = "to";
s4 += "m";
Console.Write(object.ReferenceEquals(s3, s4)); //false
string s5 = String.Intern(s4);
Console.Write(object.ReferenceEquals(s3, s5)); //true
Во-первых, все, что до сих пор написано о неизменяемых строках, правильно. Но есть некоторые важные вещи, о которых не написано. В коде
string s1 = "tom";
string s2 = "tom";
Console.Write(object.ReferenceEquals(s2, s1)); //true
действительно отображается «True», но только из-за небольшой оптимизации компилятора или как здесь, потому что CLR игнорирует атрибуты компилятора C # (см. Книгу «CLR через C #») и помещает только одну строку «tom»
в куче.
Во-вторых, вы можете исправить ситуацию с помощью следующих строк:
s3 = String.Intern(s3);
s4 = String.Intern(s4);
Console.Write (object.ReferenceEquals (s3, s4)); //true
Функция String.Intern
вычисляет хеш-код строки и ищет такой же хеш во внутренней хеш-таблице. Поскольку он находит это, он возвращает ссылку на уже существующий объект String
. Если строка не существует во внутренней хеш-таблице, создается копия строки и вычисляется хэш. Сборщик мусора не освобождает память для строки, потому что на нее ссылается хеш-таблица.
В C # каждая строка представляет собой отдельный объект и не может быть изменена. Вы создаете ссылки на них, но каждая строка отличается. Поведение последовательное и понятное.
Могу я предложить изучить класс StringBuilder
для управления строками без создания новых экземпляров? Этого должно хватить на все, что вы хотите делать со строками.
Строки неизменяемы. Это означает, что их содержимое нельзя изменить.
Когда вы выполняете s4 + = "m";
внутри, CLR копирует строку в другое место в памяти, которое содержит исходную строку и добавленную часть.
См. Ссылка на строку MSDN .