коллеги!
хотела бы спросить вопросы, которые у меня возникли при более подробном изучении языка .NET и трудности касаются как минимум ссылочного типа string
1) Intern pool; (пулСтрок) Позволяет объединить строки с одинаковыми значениями в них в определенный пул памяти. В данном случае мы инициализировали две строки с одинаковыми значениями и они попали в один пул памяти. пример в моем понимании:
string a = "aaa";
string b = "aaa";
bool c = (object)a == (object) b; // И получаем true
ОДНАКО код:
string a = "aaa";
string b = "aa";
b+="a";
bool c = (object)a == (object) b; // И получаем false
Почему так? и как это работает? по сути мы получаем строку с одинаковыми значениями и все они должны ссылаться на один объект. почему мы по итогу получаем разные результаты? Я понимаю, что в данном случае мы сравниваем объекты, а не содержимое. Но почему они не объединились?
В первом вашем примере строки интернируются на этапе компиляции. Если посмотреть в утилите ILDASM, то в окне MetaInfo в разделе User Strings будет представлен всего один экземпляр:
70000001 : ( 3) L"aaa"
Соответственно, обе переменные: и a, и b будут указывать на этот адрес.
Во втором примере в результате конкатенации тоже получается строка "aaa", но она не заносится по умолчанию в пул интернированных строк, потому что для этого нужны дополнительные проверки, то есть тратится время.
Во втором примере можно добавить строку в пул вручную:
string a = "aaa";
string b = "aa";
b += "a";
b = string.Intern(b);
bool c = (object)a == (object)b; // True
Ответ на вопрос из комментария:
Я не уверен, но, думаю, выгода в том, что после интернирования переменная b станет указывать на тот же участок памяти, что и a. После чего сборщик мусора сможет убрать другой экземпляр строки "aaa". Это почти бессмысленно для коротких строк, но может оказаться выгодно для длинных долгоживущих строк.
Также нужно учитывать, что удалить строку из пула невозможно. Она так и будет висеть в памяти до конца работы приложения. Именно поэтому в рантайме строки не добавляются в пул по умолчанию.
Чтобы не гадать, глянем сразу IL-инструкции которые генерирует компилятор:
IL_0000: nop
IL_0001: ldstr "aaa"
IL_0006: stloc.0 // a
IL_0007: ldstr "aaa"
IL_000C: stloc.1 // b
IL_000D: ldloc.0 // a
IL_000E: ldloc.1 // b
IL_000F: ceq
IL_0011: stloc.2 // c
IL_0012: ldstr "aaa"
IL_0017: stloc.3 // a2
IL_0018: ldstr "aa"
IL_001D: stloc.s 04 // b2
IL_001F: ldloc.s 04 // b2
IL_0021: ldstr "a"
IL_0026: call System.String.Concat
IL_002B: stloc.s 04 // b2
IL_002D: ldloc.3 // a2
IL_002E: ldloc.s 04 // b2
IL_0030: ceq
IL_0032: stloc.s 05 // c2
IL_0034: ret
Как видим в данном случае в первом блоке мы записали одну и ту же строку в a(IL_0006) и b(IL_000C), и в данном случае строка является интернированной, во втором же блоке только 'a2'(IL_0017) является интернированной, b2 после конкатенации (IL_0026) и присваивания (IL_002B) таковой не является.
UPD: Следующий код также возвращает false:
Console.WriteLine(ReferenceEquals(b2, a2));
Метод ReferenceEquals определяет, совпадают ли указанные экземпляры Object.
При сравнении строк. Если objA и objB являются строками, ReferenceEquals возвращает true если строки интернированы. Он не выполняет проверку на равенство значений. В следующем примере s1 и s2 равны, поскольку они являются двумя экземплярами одной интернированной строки. Тем не менее s3 и s4 не равны, поскольку несмотря на то, что они имеют идентичные строковые значения, эти строки не интернированы.
using System;
public class Example
{
public static void Main()
{
String s1 = "String1";
String s2 = "String1";
Console.WriteLine("s1 = s2: {0}", Object.ReferenceEquals(s1, s2));
Console.WriteLine("{0} interned: {1}", s1,
String.IsNullOrEmpty(String.IsInterned(s1)) ? "No" : "Yes");
String suffix = "A";
String s3 = "String" + suffix;
String s4 = "String" + suffix;
Console.WriteLine("s3 = s4: {0}", Object.ReferenceEquals(s3, s4));
Console.WriteLine("{0} interned: {1}", s3,
String.IsNullOrEmpty(String.IsInterned(s3)) ? "No" : "Yes");
}
}
// The example displays the following output:
// s1 = s2: True
// String1 interned: Yes
// s3 = s4: False
// StringA interned: No
P.s.:
Метод также возвратит false при сравнении типов значений, если objA и objB являются типами значений, упакованы перед передачей в ReferenceEquals и
представляют одно и то же значение.
int int1 = 3;
Console.WriteLine(Object.ReferenceEquals(int1, int1)); //False
Console.WriteLine(int1.GetType().IsValueType); //True
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости