Несогласованность результатов String.Intern и возможные варианты использования

        string s1 = new string (new char[] { 'H', 'e', 'l', 'l', 'o' });
        string i1 = string.Intern(s1);
        bool result1 = ReferenceEquals(s1, i1);

        string s2 = new string(new char[] { 'H', 'e', 'l', 'l', 'o' });
        string i2 = string.Intern(s2);
        bool result2 = ReferenceEquals(s2, i2);

        bool result3 = ReferenceEquals(i1, i2);
        Console.WriteLine(result1); // Should be False
        Console.WriteLine(result2); // Should be False
        Console.WriteLine(result3); // Should be True

Результат этого небольшого теста: Правда, Ложь, Правда на моем компьютере и этом онлайн-компиляторе , но Ложь, Ложь, Правда в этом онлайн-компиляторе. Я спросил об этом GPT, и он указал на то, что второй результат также верен.

Если я правильно понял, s1 создает в куче новый строковый объект (который автоматически регистрируется в пуле стажеров) и хранит ссылку на этот строковый объект. i1 проверяет ЗНАЧЕНИЕ s1, чтобы узнать, существует ли он в пуле стажеров, и, поскольку это так, i1 берет ссылку из пула стажеров, заставляя s1 и i1 ссылаться на один и тот же строковый объект.

Аналогично, s2 создает в куче еще один строковый объект с тем же значением, что и s1 (хотя я не уверен, добавляется ли он сразу в пул стажеров). i2 проверяет ЗНАЧЕНИЕ s2 во внутреннем пуле, обнаруживает, что этот строковый объект уже создан, и, таким образом, берет ссылку из внутреннего пула. В результате i1 и i2 ссылаются на один и тот же объект и возвращают true.

Буду рад, если кто-нибудь объяснит, как работает стажер и почему результаты различаются? И каков вариант использования этой функции в командной среде?

PS: Я запускаю его в .net 7 в локальном и 8 в онлайн-компиляторах.

Вторая ссылка («onlinegdb») даже не позволяет мне скомпилировать пример, поскольку она, похоже, не понимает новые выражения целевого типа.

Lennart 27.05.2024 13:37

@Lennart, о, это потому, что это должен быть C# версии 7 или выше, я думаю, я это исправлю.

Furkan Bilal 27.05.2024 13:46

Если вы запустите его с помощью NET Framework 4.7.2 в NETFiddle, это также будет False, False, True. Это вопрос версии среды выполнения .NET.

lidqy 27.05.2024 13:49

Код, который принят в качестве ответа, является источником моего вопроса, потому что у меня были разные результаты с разными компиляторами в Интернете, и я также хотел знать, есть ли у кого-нибудь необходимость использовать эту неясную функцию. @КлаусГюттер

Furkan Bilal 27.05.2024 13:55

почему вы не использовали кнопку «Поделиться» во втором примере компилятора? onlinegdb.com/HnCybTQGdo

Rand Random 27.05.2024 14:48
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Отказ от ответственности: я тестировал это только на .NET 8 в режиме отладки. Вывод, который я получаю, — «истина», «ложь», «истина», и это можно объяснить, проследив это в WinDbg.

Поведение следующее: создание строки из char[] не интернирует строку автоматически. Только при вызове Intern ссылка добавляется в таблицу стажеров. Когда вы вызываете Intern, таблица сканируется на наличие совпадений, и если совпадение найдено, возвращается ссылка. В противном случае он добавляется в таблицу, а затем возвращается.

Изначально мы создаем строку из char[]. Эта строка не интернируется средой выполнения, поскольку не считается строковым литералом. Перечислив местных жителей, я получаю следующее:

  LOCALS:
    0x000000B35897E858 = 0x000002a4c0440708 <- this is the ref for our string

Выполнив gcroot, я вижу, что он коренится только по локальной ссылке. Т.е. это не интернировано.

0:000> !gcroot 000002a4c0440708
Caching GC roots, this may take a while.
Subsequent runs of this command will be faster.

Thread 49b4:
    rbp+50: 000000b35897e810
      -> 02a4c0440708     System.String 

    rbp+98: 000000b35897e858
      -> 02a4c0440708     System.String 

Затем мы интернируем эту строку и возвращаем ссылку на интернированную строку. Отладчик показывает, что оба локальных адреса указывают на один и тот же адрес:

   LOCALS:
    0x000000B35897E858 = 0x000002a4c0440708
    0x000000B35897E850 = 0x000002a4c0440708

gcroot теперь указывает дополнительный корень.

0:000> !gcroot 000002a4c0440708

HandleTable:
    000002a4bbf712d0 (strong handle)
          -> 02a4bdc07ff0     System.Object[] 
          -> 02a4c0440708     System.String 

Прочная ручка – стол для стажеров. Это означает, что ссылка выше была добавлена ​​в статический массив, который является внутренней таблицей. Однако он не меняет ссылку, а просто добавляет ее, так что теперь строка также коренится в таблице стажеров.

Это соответствует выводу эталонного сравнения.

Далее мы генерируем новую строку. Поскольку это обычная строка, она размещается в куче и получает другой адрес.

PARAMETERS:
    args (0x000000B35897E880) = 0x000002a4bfc08308
LOCALS:
    0x000000B35897E858 = 0x000002a4c0440708
    0x000000B35897E850 = 0x000002a4c0440708
    0x000000B35897E84C = 0x0000000000000001
    0x000000B35897E840 = 0x000002a4c0440790 <--- this is s2

Я аннотировал дамп выше, чтобы показать s2. (третий адрес содержит логическое значение первого сравнения, т. е. result1.

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

PARAMETERS:
    args (0x000000B35897E880) = 0x000002a4bfc08308
LOCALS:
    0x000000B35897E858 = 0x000002a4c0440708
    0x000000B35897E850 = 0x000002a4c0440708
    0x000000B35897E84C = 0x0000000000000001
    0x000000B35897E840 = 0x000002a4c0440790
    0x000000B35897E838 = 0x000002a4c0440708

Это означает, что адреса s2 и i2 различаются, что соответствует выводу false.

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

Спасибо за подробное объяснение темы. Сталкивались ли вы когда-нибудь с этой функцией в производственной среде или чувствовали необходимость ее использовать?

Furkan Bilal 28.05.2024 16:53

@FurkanBilal Добро пожаловать. Все литеральные строки по умолчанию интернированы, поэтому в этом смысле все используют его, но поскольку нет возможности удалить интернированные строки, делать это для динамически генерируемых строк редко бывает хорошей идеей.

Brian Rasmussen 29.05.2024 07:09

Другие вопросы по теме