Мне, честно говоря, было пофигу, где эти строки формируются и хранятся, главное, что это все работает. Я воспринимал это как аксиому, которую, точнее, нужно сформулировать так: «Все строковые литералы компилятор C# помещает во внутренний пул».
Но, когда я писал код, похожий на следующий код, то немного призадумался:
string name = textReader.ReadLine();
switch (name)
{
case "Bob":
Console.WriteLine("Hello, Bob!");
break;
case "Vasya":
Console.WriteLine("Privet, Vasya!");
break;
default:
Console.WriteLine("O_o Vasya!");
break;
}
Ведь в переменной name содержится строка, прочитанная из файла в момент выполнения, так как же эти строковые кейсы работают? Они же, по идеи, должны сравнивать ссылки. А чтобы моя переменная name совпала хотя бы с одним этим кейсом, то она тоже должна быть интернирована (помещена в пул), как и строковые литералы.
Оказывается, что да. И в каком месте это происходит, я пока еще не знаю. Но, строковые литералы помещаются в пул в момент компиляции, а новые, динамические строки взаимодействуют с этим пулом в момент выполнения. Получается, что каждая новая строка сравнивается со всеми уже существующими, что может влиять на производительность кода.
Я наивно пытался похачить это дело, но все-таки ничего не получилось:
using (TextReader tr = File.OpenText(@"C:\temp\strings.txt "))
{
string string1 = tr.ReadLine(); // This is a string
string string2 = tr.ReadLine(); // This is a string
string string3 = tr.ReadLine(); // This is another string
Console.WriteLine("First try:");
Console.WriteLine(" string1={0}\n string2={1}\n string3={2}", string1.GetHashCode(),
string2.GetHashCode(),
string3.GetHashCode());
StringBuilder sb = new StringBuilder();
string internPool = "Intern Pool";
sb.Append("Intern ").Append("Pool");
Console.WriteLine("Second try:\n string1={0}\n string2={1}", internPool.GetHashCode(),
sb.GetHashCode());
Console.WriteLine("Third try:\n string1={0}\n string2={1}", internPool.GetHashCode(),
sb.ToString().GetHashCode());
}
Текстовый файл:
This is a string
This is a string
This is another string
Вывод первой попытки, как не странно string1 и string2 равны:
First try:
string1=2035671784
string2=2035671784
string3=164170943
Вывод второй попытки! Ага все таки не равны! Но, на самом деле и типы тут разные:
Second try:
string1=-1024490162
string2=58225482
Вывод третий попытки. Приведем-ка это это добро .ToString. :( Все таки ссылки равны:
Third try:
string1=-1024490162
string2=-1024490162
Upd. Вот что значит дописывать код в 2 часа ночи :) На самом деле, ссылки не равны, а .GetHashCode это не правильный способ. Спасибо, Alex, за комментарий.
Если добавить следующие строки кода, то выясняется, что ссылки действительно не равны:
Console.WriteLine("Ref Eq string1 and string2 = {0}", object.ReferenceEquals(string1, string2));
Console.WriteLine("Ref Eq string2 and string3 = {0}", object.ReferenceEquals(string2, string3));
// и...
Console.WriteLine("Ref Eq internPool and sb.ToString() = {0}", object.ReferenceEquals(internPool, sb.ToString()));
Результат:
Ref Eq string1 and string2 = False
Ref Eq string2 and string3 = False
Ref Eq internPool and sb.ToString() = False
2 коммент.:
[q]Ведь в переменной name содержится строка, прочитанная из файла в момент выполнения, так как же эти строковые кейсы работают? Они же, по идеи, должны сравнивать ссылки[/q]
Операция == для класса String сравнивает не ссылки, а значения!!!! Собственно это переопределенная операция. А вообще для любого объекта есть 1 метод ReferenceEquals, 2 метода Equals (по сути идентичные), и операция ==.
Во всех твоих примерах ты сравниваешь не ссылки а GetHashCode(). Эта операция тоже переопределена для String, так что hash берется не от ссылки, а от значения и когда хэши равны - это означает, что равны их значения (ссылки не обязательно равны. В твоем случае они все разные!!!).
[q]Но, строковые литералы помещаются в пул в момент компиляции, а новые, динамические строки взаимодействуют с этим пулом в момент выполнения.[/q]
В момент компиляции строковые литералы в пул не помещаются... Во время сравнения строк происходит упаковка (boxing) литералов в ссылочный тип, происходит сравнение двух ссылочных типов, а после сравнения, этот упакованный объект уничтожается сборщиком мусора.
Спасибо, Alex, все оказывается действительно так, как вы говорите. Вы спасли мой Mosk ;)
Отправить комментарий