Comments 10
Это вполне очевидное поведение для интернированых строк, куда входят все строковые литералы. Вполне можно "украсть" и заменить чужой литерал через string.Intern()
Это не является какой-то проблемой unsafe кода, это проблема тех кто мутирует общие ресурсы объявленные иммутабельными. Аналогично можно скастить IReadOnlyCollection<T>
к T[]
и изменить массив, а потом обвинять механизм каста в том что он помог нарушить контракт.
Со строкой вполне можно создать новую через `new string('\0', 100)` и спокойно ее менять.
Вы описали часть того, для чего именно существует небезопасный контекст в .NET. То что через небезопасный код можно менять интернированные строки -- не новости. И описанные вами проблемы не являются проблемами как таковыми.
Если вы читали Рихтера, то должны помнить вот эту часть:
По умолчанию компилятор C# компании Microsoft генерирует безопасный код. Под этим термином понимается код, безопасность которого подтверждается в процессе верификации. Тем не менее компилятор Microsoft C# также позволяет разработчикам писать небезопасный код, способный напрямую работать с адресами памяти и манипулировать с байтами по этим адресам. Как правило, эти чрезвычайно мощные средства применяются для взаимодействия с неуправляемым кодом или для оптимизации алгоритмов, критичных по времени.
Однако использование небезопасного кода создает значительный риск: небезопасный код может повредить структуры данных и использовать (или даже создавать) уязвимости в системе безопасности.
Собственно, unsafe обозначает, что своими кривыми руками можно сломать все, что вздумается. ССЗБ.
Про ваш пример в листинге 3.2: из-за JIT-компиляции подгрузилась новая строка, потому что в кэше не нашлась эта же строка. Механизм интернирования на стороне CoreCLR вы можете посмотреть здесь: https://github.com/dotnet/runtime/blob/319391eb90065ec2aca29c6de0046e47a1286016/src/coreclr/vm/stringliteralmap.cpp#L403
Мы не можем изменить ссылку, но значение по ссылке изменить проще простого.
К слову, того же можно добиться рефлексией:
typeof(ImmutablePerson)
.GetField("<Name>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(person, "Michael");
Совет тем, кто захочет переворачивать строки. Помните о суррогатных парах.
У меня такие задачи люди на скрининге перед собесом решают. Придётся менять задачу, становится слишком легко найти ответ :/
А в реальных проектах часто unsafe код и рефлексия встречается? На какую должность такие вопросы задают?
В библиотеках, latency critical систем, например сериализации, маршрутизации и иной инфраструктуры -да. В остальном нет. В коде прикладного уровня, который писали мои команды,мы такого не допускали. Но задачи собеседования на то и задачи, чтобы тестировать экспертизу, понимание платформы и прочее. Все мы желаем работать с ребятами, которые понимают, что происходит в их коде.
[StructLayout(LayoutKind.Explicit)]
struct BiteData {
[FieldOffset(0)] byte[] bytes;
[FieldOffset(0)] Data data;
}
А в чем могут быть проблемы с преобразованием типов с помощью этого? Это не unsafe, и по другому сделать сложно, чтоб без лишнего выделения памяти, мусора и быстро.
Проблемы unsafe кода C#