Чому струни не можна змінювати в Java та .NET?


191

Чому саме вони вирішили зробити Stringнепорушними в Java та .NET (та деяких інших мовах)? Чому вони не зробили його змінним?


13
Я мав таку ж думку, але перевірив місце розташування плакатів і виявив, що вони з Бельгії. Враховуючи, що це означає, що вони, ймовірно, не будуть носіями англійської мови. У поєднанні з тим, що більшість тубільців нерозуміє мову, я вирішив скоротити її.
belugabob

8
Дякую, белубобоб, але я не її я його. Мабуть, люди не враховують культурних відмінностей.
chrissie1

7
Мої вибачення - Кріссі (як правило) ім'я дівчини у Великобританії - що робить мене жертвою ще однієї культурної різниці :-)
belugabob

Просто зауважте, що в .NET Stringнасправді змінюється всередині країни. StringBuilderу .NET 2.0 мутує рядок . Я просто залишу його тут.
Елвін Вонг

Насправді. NET-рядки є змінними. І це навіть не хек злому.
Bitterblue

Відповіді:


205

Згідно Ефективної Java , глава 4, сторінка 73, 2-е видання:

"Для цього є багато вагомих причин. Незмінні класи легше проектувати, реалізовувати та використовувати, ніж класи, що змінюються. Вони менш схильні до помилок і більш безпечні.

[...]

" Незмінні об'єкти прості. Незмінний об'єкт може знаходитися в точно одному стані, стані, в якому він був створений. Якщо ви переконаєтесь, що всі конструктори встановлюють інваріанти класів, то гарантується, що ці інваріанти залишаться вірними протягом усього часу, ніяких зусиль з вашого боку.

[...]

Змінювані об'єкти за своєю суттю є безпечними для потоків; вони не потребують синхронізації.Вони не можуть бути пошкоджені кількома потоками, які одночасно отримують доступ до них. Це далеко не найпростіший підхід до досягнення безпеки ниток. Насправді жодна нитка ніколи не може спостерігати жодного впливу іншої нитки на незмінний об’єкт. Тому незмінними об'єктами можна ділитися вільно

[...]

Інші невеликі моменти з цієї ж глави:

Ви не тільки можете ділитися незмінними об'єктами, але й можете ділитися їх внутрішніми.

[...]

Незмінні об'єкти створюють великі будівельні блоки для інших об'єктів, будь то змінні чи незмінні.

[...]

Єдиним реальним недоліком незмінних класів є те, що вони вимагають окремого об'єкта для кожного окремого значення.


22
Прочитайте друге речення моєї відповіді: Незмінні класи легше проектувати, впроваджувати та використовувати, ніж класи, що змінюються. Вони менш схильні до помилок і більш безпечні.
ПЛІФ ПРИНЦЕСІ

5
@PRINCESSFLUFF Додам, що спільний доступ до змінних рядків небезпечний навіть на одній нитці. Наприклад, копіювання звіту: report2.Text = report1.Text;. Потім, де - то ще, змінюючи текст: report2.Text.Replace(someWord, someOtherWord);. Це змінило б і перший звіт, і другий.
фог

10
@ Сам він не запитував "чому вони не можуть бути зміненими", він запитав "чому вони вирішили зробити незмінними", на що це відповідає ідеально.
Джеймс

1
@PRINCESSFLUFF Ця відповідь не стосується рядків. Це було питання ОП. Це так засмучує - це постійно відбувається на SO та з String-питаннями щодо незмінюваності. Відповідь тут говорить про загальні переваги незмінності. Тож чому не всі типи незмінні? Чи можете ви повернутися та адресу String?
Howiecamp

@Howiecamp Я думаю, що це мається на увазі у відповідь про те, що рядки могли бути мінливими (ніщо не заважає існувати гіпотетичному класові змінних рядків). Вони просто вирішили не робити цього так для простоти, і тому, що це охоплювало 99% випадків використання. Вони все ж надавали StringBuilder для інших 1% випадків.
Даніель Гарсія Рубіо

102

Причин, як мінімум, дві.

По-перше - безпека http://www.javafaq.nu/java-article1060.html

Основною причиною, чому String зробив непорушною, була безпека. Подивіться на цей приклад: у нас є метод відкриття файлу з перевіркою входу. Ми передаємо String цьому методу для обробки автентифікації, яка необхідна до того, як виклик буде переданий ОС. Якщо String був змінним, можна було якось змінити його вміст після перевірки автентичності, перш ніж ОС отримає запит від програми, тоді можна запитати будь-який файл. Отже, якщо ви маєте право відкривати текстовий файл у каталозі користувачів, але тоді на лету, коли вам якось вдасться змінити ім'я файлу, ви можете подати запит на відкриття "passwd" файла чи будь-якого іншого. Тоді файл можна змінити, і можна буде ввійти безпосередньо в ОС.

Друге - ефективність пам’яті http://hikrish.blogspot.com/2006/07/why-string-class-is-immutable.html

JVM внутрішньо підтримує "Струнний басейн". Для досягнення ефективності пам'яті JVM передасть об'єкт String з пулу. Він не створить нових об'єктів String. Отже, щоразу, коли ви створюєте новий літеральний рядок, JVM перевірятиме в пулі, чи він вже існує чи ні. Якщо ви вже наявні в пулі, просто дайте посилання на той самий об’єкт або створіть новий об’єкт у пулі. Буде багато посилань, які вказують на ті самі об'єкти String, якщо хтось змінить значення, це вплине на всі посилання. Отже, сонце вирішило зробити це непорушним.


Це хороший момент щодо повторного використання, і особливо це стосується String.intern (). Можна було б повторно використовувати, не роблячи непорушними всі рядки, але життя, як правило, ускладнюється в цей момент.
веселість

3
Жоден із них не здається мені надзвичайно вагомим причин у цей день та вік.
Брайан Кноблауш

1
Я не надто переконаний аргументом ефективності пам’яті (тобто, коли два або більше об'єктів String діляться одними і тими ж даними, а один модифікується, то обидва змінюються). Об'єкти CString у MFC обходять це за допомогою підрахунку посилань.
RobH

7
безпека насправді не є частиною Raison d'être для незмінних рядків - ваша ОС буде копіювати рядки в буфери режиму ядра і робити там перевірку доступу, щоб уникнути атак часу. Це справді все про безпеку та ефективність потоку :)
snemarch

1
Аргумент ефективності пам'яті також не працює. У рідній мові, як C, рядкові константи - це просто вказівники на дані в ініціалізованому розділі даних - вони все одно є лише для читання / незмінними. "якщо хтось змінює значення" - знову ж таки, рядки з пулу в будь-якому разі читаються лише для читання.
wj32

57

Насправді, причини рядків незмінні в Java не мають великого відношення до безпеки. Дві основні причини:

Безпека Теди:

Струни - надзвичайно широко використовуваний тип об’єкта. Тому більш-менш гарантовано використання в багатопотоковому середовищі. Рядки незмінні, щоб переконатися, що безпечно ділити рядки між потоками. Наявність непорушних рядків гарантує, що при передачі рядків з потоку A в інший потік B, потік B не може несподівано змінити рядок нитки A.

Це не тільки допомагає спростити і без того складне завдання багатопотокового програмування, але й допомагає виконувати багатопотокові програми. Доступ до об'єктів, що змінюються, повинен якось синхронізуватися, коли до них можна отримати доступ з декількох потоків, щоб переконатися, що один потік не намагається прочитати значення вашого об'єкта, коли він змінюється іншим потоком. Належну синхронізацію - як важко зробити правильно програмісту, так і дорогу під час виконання. Змінювані об’єкти не можуть бути змінені, а тому синхронізація не потребує.

Продуктивність:

У той час як згадувалося про інтернування в String, це лише незначне збільшення ефективності пам'яті для програм Java. Інтерновані лише рядкові рядки. Це означає, що лише ті рядки, які є однаковими у вихідному коді, матимуть спільний об'єкт String Object. Якщо ваша програма динамічно створює однакові рядки, вони будуть представлені в різних об'єктах.

Що ще важливіше, незмінні рядки дозволяють їм ділитися внутрішніми даними. Для багатьох рядкових операцій це означає, що основний масив символів не потрібно копіювати. Наприклад, скажіть, що ви хочете взяти п’ять перших символів String. На Java ви б назвали myString.substring (0,5). У цьому випадку те, що робить метод substring () - це просто створити новий об'єкт String, який розділяє основний char myString [], але хто знає, що він починається з індексу 0 і закінчується в індексі 5 цього char []. Щоб сказати це у графічній формі, ви закінчите таке:

 |               myString                  |
 v                                         v
"The quick brown fox jumps over the lazy dog"   <-- shared char[]
 ^   ^
 |   |  myString.substring(0,5)

Це робить цей вид операцій надзвичайно дешевим, і O (1), оскільки операція не залежить ні від довжини початкової рядка, ні від довжини підрядки, яку нам потрібно витягти. Така поведінка також має деякі переваги пам’яті, оскільки багато рядків можуть поділяти свої основні символи [].


6
Реалізація підрядів як посилань, які поділяють основні, char[]є досить сумнівним дизайнерським рішенням. Якщо ви читаєте в цілому файлі в один рядок і зберігаєте посилання лише на 1-символьну підрядку, весь файл повинен зберігатися в пам'яті.
Гейб

5
Точно я натрапив на цю конкретну ґутчу під час створення веб-сканера, який потребував лише вилучення кількох слів з усієї сторінки. HTML-код усієї сторінки був у пам'яті, і завдяки спільному рядку розділення char [] я зберігав весь HTML-код, хоча мені було потрібно лише кілька байт. Вирішенням цього є використання нового String (original.substring (.., ..)), конструктор String (String) робить копію відповідного діапазону базового масиву.
LordOfThePigs

1
Додаток, який охоплює наступні зміни: Оскільки Jave 7 String.substring()виконує повну копію, щоб запобігти проблемам, згаданим у коментарях вище. У Java 8 два поля, що дозволяють char[]використовувати спільний доступ, а саме countі offset, видаляються, таким чином зменшуючи слід пам'яті екземплярів String.
Крістіан Семрау

Я погоджуюсь із частиною Thead Safety, але сумніваюсь у випадку підрядки.
Gqqnbig

@LoveRight: Потім перевірте вихідний код java.lang.String ( grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/… ), так було все до Java 6 (який був чинним, коли ця відповідь була написана). Я, мабуть, змінився в Java 7.
LordOfThePigs

28

Безпека нитки та продуктивність. Якщо рядок неможливо змінити, це безпечно та швидко пройти посилання навколо декількох потоків. Якби рядки були змінними, вам завжди доведеться скопіювати всі байти рядка в новий екземпляр або забезпечити синхронізацію. Типова програма буде читати рядок 100 разів за кожен раз, коли рядок потрібно змінювати. Дивіться вікіпедію про незмінність .


11

Справді слід запитати: "чому X повинен бути змінним?" Краще за замовчуванням незмінність через переваги, про які вже згадувала принцеса Фуфф . Має бути винятком, що щось змінюється.

На жаль, більшість сучасних мов програмування за замовчуванням змінюється, але, сподіваємось, у майбутньому дефолт буде більше на незмінність (див . Список бажань для наступної основної мови програмування ).


7

Оце Так! Я не можу повірити дезінформації тут. Strings незмінні нічого не мають із захистом. Якщо хтось уже має доступ до об'єктів у запущеній програмі (про що варто було б припустити, якщо ви намагаєтесь захистити від того, щоб хтось "зламав" Stringу вашому додатку), це, безумовно, буде безліччю інших можливостей для злому.

Цілком нова ідея, що незмінність - Stringце вирішення проблем з ниткою. Гммм ... у мене є об'єкт, який змінюється двома різними потоками. Як вирішити це? синхронізувати доступ до об’єкта? Naawww ... не давайте нікому взагалі міняти об’єкт - це вирішить усі наші брудні проблеми з одночасністю! Насправді давайте зробимо всі об'єкти непорушними, і тоді ми зможемо видалити синхронізований набір з мови Java.

Справжня причина (на яку вказували інші вище) - оптимізація пам'яті. Досить часто зустрічається в будь-якій програмі, коли один і той же літеральний рядок може використовуватися повторно. Насправді це так часто, що десятиліття тому багато компіляторів зробили оптимізацію зберігання лише одного екземпляра Stringбуквалу. Недолік цієї оптимізації полягає в тому, що код виконання, який модифікує Stringлітерал, вводить проблему, оскільки він модифікує екземпляр для всіх інших кодів, якими він ділиться. Наприклад, було б непогано, щоб функція десь у додатку змінювала Stringбуквальне значення "dog"на "cat". А printf("dog")призведе до літералів (тобто зробить їх незмінними). Деякі компілятори (з підтримкою ОС) могли б досягти цього шляхом розміщення"cat" записується на стандартний висновок. З цієї причини потрібен був спосіб захисту від коду, який намагається змінитиStringString буквально в спеціальний сегмент пам'яті для читання, який може викликати помилку пам'яті, якщо буде зроблена спроба запису.

На Java це відоме як інтернування. Компілятор Java тут лише слідує стандартній оптимізації пам'яті, яку роблять компілятори десятиліттями. І для вирішення тієї ж проблеми цих Stringлітералів, які модифікуються під час виконання, Java просто робить Stringклас незмінним (т. Е. Не дає вам сетерів, які дозволяли б змінювати Stringвміст). Strings не повинні були бути непорушними, якби Stringне відбулося інтернування літераторів.


3
Я категорично не згоден з незмінністю та нитки коментарів, мені здається, ви не зовсім розумієте це. І якщо Джош Блох, один із впроваджувачів Java, каже, що це було одним із проблем дизайну, то як це може бути дезінформацією?
javashlook

1
Синхронізація дорога. Посилання на об'єкти, що змінюються, повинні бути синхронізованими, а не для незмінних. Це причина зробити всі об'єкти незмінними, якщо вони не мають бути зміненими. Струни можуть бути непорушними, а тому, завдяки цьому вони роблять їх більш ефективними в декількох потоках.
Девід Торнлі

5
@Jim: Оптимізація пам’яті - це не причина «THE», це причина «A». Безпека ниток також є причиною "A", оскільки незмінні об'єкти за своєю суттю є безпечними для потоків і не потребують дорогої синхронізації, як згадував Девід. Безпека нитки насправді є побічним ефектом незмінного об'єкта. Ви можете розглядати синхронізацію як спосіб зробити об'єкт "тимчасово" незмінним (ReaderWriterLock зробить його лише для читання, а звичайний замок зробить його зовсім недоступним, що, звичайно, зробить його також непорушним).
Трайнко

1
@DavidThornley: Створення декількох незалежних довідкових контурів до власника змінного значення ефективно перетворює його на сутність, і робить його набагато складніше міркувати навіть осторонь проблем з потоком. Як правило, змінні об'єкти є більш ефективними від непорушних у тих випадках, коли для кожного існуватиме точно один опорний шлях, але незмінні об'єкти дозволяють ефективно обмінюватися вмістом об'єктів шляхом спільного використання посилань. Найкращий зразок пояснюється Stringі StringBuffer, але, на жаль, мало інших типів слідують цій моделі.
supercat

7

String не є примітивним типом, але ви зазвичай хочете використовувати його з семантикою значення, тобто як значення.

Цінність - те, чому ви можете довіряти, не зміниться за вашою спиною. Якщо ви пишете: String str = someExpr(); Ви не хочете, щоб це змінилося, якщо ви щось не зробите str.

Stringяк це Objectмає природно вказівна семантика, щоб отримати також значення семантики, вона також повинна бути незмінною.


7

Одним із факторів є те, що, якби Strings були змінними, об’єкти, що зберігають Strings, повинні бути обережними для зберігання копій, щоб не змінити їх внутрішні дані без попереднього повідомлення. Зважаючи на те, що Strings - це досить примітивний тип, подібний до чисел, приємно, коли можна ставитися до них так, ніби вони передані за значенням, навіть якщо вони передаються за посиланням (що також допомагає заощадити на пам'яті).


6

Я знаю, що це удар, але ... Чи справді вони незмінні? Розглянемо наступне.

public static unsafe void MutableReplaceIndex(string s, char c, int i)
{
    fixed (char* ptr = s)
    {
        *((char*)(ptr + i)) = c;
    }
}

...

string s = "abc";
MutableReplaceIndex(s, '1', 0);
MutableReplaceIndex(s, '2', 1);
MutableReplaceIndex(s, '3', 2);
Console.WriteLine(s); // Prints 1 2 3

Ви навіть можете зробити це методом розширення.

public static class Extensions
{
    public static unsafe void MutableReplaceIndex(this string s, char c, int i)
    {
        fixed (char* ptr = s)
        {
            *((char*)(ptr + i)) = c;
        }
    }
}

Що робить наступну роботу

s.MutableReplaceIndex('1', 0);
s.MutableReplaceIndex('2', 1);
s.MutableReplaceIndex('3', 2);

Висновок: вони перебувають у незмінному стані, про який знає компілятор. Звернене вище, стосується лише рядків .NET, оскільки у Java немає вказівників. Однак рядок можна повністю змінити за допомогою покажчиків на C #. Справа не в тому, як призначені для використання покажчики, практичне їх використання чи безпечне використання; однак це можливо, таким чином, згинаючи все правило, що змінюється. Зазвичай ви не можете змінювати індекс безпосередньо рядка, і це єдиний спосіб. Існує спосіб, що це можна запобігти, забороняючи вказівні екземпляри рядків або створюючи копію, коли рядок вказується на, але жоден з них не робиться, що робить рядки в C # не зовсім незмінними.


1
+1. .NET рядки насправді не змінюються; насправді це робиться весь час в класах String і StringBuilder з перфліктних причин.
Джеймс Ко

3

Для більшості цілей "рядок" є (використовується / трактується як / вважається / вважається) значущою атомною одиницею, як і число .

Запитувати, чому окремі символи рядка не змінюються, це як запитання, чому окремі біти цілого числа не змінюються.

Ви повинні знати, чому. Просто подумайте.

Я ненавиджу це говорити, але, на жаль, ми обговорюємо це, тому що наша мова засихає, і ми намагаємось використати одне слово, рядок , щоб описати складне, контекстуально розташоване поняття або клас об'єкта.

Ми виконуємо обчислення та порівняння зі "рядками", аналогічними тому, як ми робимо з числами. Якщо рядки (або цілі числа) були змінними, нам доведеться написати спеціальний код, щоб заблокувати їх значення в незмінних локальних формах, щоб надійно виконати будь-який вид обчислення. Тому найкраще думати про такий рядок, як числовий ідентифікатор, але замість того, щоб він був довгим 16, 32 або 64 біт, це може бути довгі сотні біт.

Коли хтось каже «рядок», ми всі думаємо про різні речі. Ті, хто думає про це просто як набір персонажів, не маючи на увазі певної мети, звичайно, будуть здивовані тим, що хтось просто вирішив, що їм не вдається маніпулювати цими персонажами. Але клас "string" - це не просто масив символів. Це - STRINGне, а не char[]. Існують деякі основні припущення щодо поняття, яке ми називаємо "рядком", і воно, як правило, може бути охарактеризовано як змістовна, атомна одиниця кодованих даних, як число. Коли люди говорять про "маніпулювання рядками", можливо, вони справді говорять про маніпулювання символами для створення рядків , і StringBuilder чудово підходить для цього.

Поміркуйте на хвилину, як це було б, якби струни були незмінними. Наступна функція API може бути підведена до повернення інформації для іншого користувача, якщо змінна рядок імені користувача навмисно або ненавмисно змінена іншим потоком, коли ця функція використовує її:

string GetPersonalInfo( string username, string password )
{
    string stored_password = DBQuery.GetPasswordFor( username );
    if (password == stored_password)
    {
        //another thread modifies the mutable 'username' string
        return DBQuery.GetPersonalInfoFor( username );
    }
}

Безпека стосується не лише "контролю доступу", а й "безпеки" та "гарантування правильності". Якщо метод не може бути легко записаний і від нього залежить надійне виконання простого обчислення або порівняння, викликати його не безпечно, але було б безпечно поставити під сумнів саму мову програмування.


У C # рядок може бути змінено за допомогою вказівника (використання unsafe) або просто через відображення (ви можете легко отримати базове поле). Це робить пункт про безпеку недійсним, оскільки кожен, хто навмисно хоче змінити рядок, може зробити це досить легко. Однак він забезпечує безпеку програмістам: якщо ви не зробите щось особливе, рядок гарантується незмінним (але це не безпечно для потоків!).
Авель

Так, ви можете змінювати байти будь-якого об'єкта даних (рядок, int тощо) через покажчики. Однак ми говоримо про те, чому клас рядка незмінний у тому сенсі, що він не має вбудованих у нього публічних методів для зміни своїх символів. Я говорив, що рядок дуже схожий на число, тому що маніпулювати окремими символами не має більшого сенсу, ніж маніпулювати окремими бітами числа (коли ви обробляєте рядок як цілий маркер (а не як байтовий масив), а число як числове значення (не як бітове поле). Ми говоримо на рівні понятійного об'єкта, а не на рівні суб-об'єкта.
Трайнко

2
І лише для уточнення, покажчики в об'єктно-орієнтованому коді по своїй суті небезпечні, саме тому, що вони обходять загальнодоступні інтерфейси, визначені для класу. Що я говорив, було те, що функцію можна легко підманути, якщо публічний інтерфейс для рядка дозволить їй змінювати інші потоки. Звичайно, це завжди може бути обмануто, отримавши доступ до даних безпосередньо за допомогою покажчиків, але не так легко чи ненавмисно.
Трайнко

1
"покажчики в об'єктно-орієнтованому коді самі по собі небезпечні", якщо ви не називаєте їх посиланнями . Посилання на Java не відрізняються від покажчиків на C ++ (лише арифметика вказівника відключена). Інша концепція - це управління пам’яттю, якою можна керувати або керувати вручну, але це різна річ. Ви можете мати опорну семантику (покажчики без арифметики), не маючи GC (навпаки, важче буде в тому сенсі, що семантику доступності було б важче зробити чистим, але не нездійсненним)
Девід Родрігес - дрибей

Інша справа, що якщо рядки майже незмінні, але це не зовсім так (я не знаю достатньо CLI тут), це може бути дуже погано з міркувань безпеки. У деяких старих реалізаціях Java ви могли це зробити, і я знайшов фрагмент коду, який використовував це для інтерналізації рядків (спробуйте знайти іншу внутрішню рядок із тим самим значенням, поділитися вказівником, видалити старий блок пам'яті) та застосував backdoor переписати вміст рядків, що змушує неправильну поведінку в іншому класі. (Розгляньте можливість написання "SELECT *" на "DELETE")
David Rodríguez - dribeas

3

Незмінність не так тісно пов'язана із безпекою. Для цього, принаймні в .NET, ви отримуєте SecureStringклас.

Пізніше редагуйте: У Java ви знайдете GuardedStringподібну реалізацію.


2

Рішення змінити рядки на C ++ викликає багато проблем, дивіться цю чудову статтю Кельвіна Генні про хворобу Mad CoW .

COW = Копіювати при записі.


2

Це торг. Strings перейдіть до Stringпулу, і коли ви створите кілька однакових Strings, вони поділяють однакову пам'ять. Дизайнери вважали, що ця технологія збереження пам’яті буде добре працювати в загальній справі, оскільки програми, як правило, багато перемелюють одні й ті ж рядки.

Мінус полягає в тому, що конкатенації роблять багато зайвих String, які є лише перехідними і просто стають сміттям, фактично шкодячи пам'яті. Ви повинні StringBufferі StringBuilder(у Java StringBuilderтакож є в .NET) використовувати для збереження пам'яті в цих випадках.


1
Майте на увазі, що "рядок рядків" не використовується автоматично для ВСІХ рядків, якщо ви явно не використовуєте "inter ()" 'рядки.
веселість

2

Strings на Java не справді незмінні, ви можете змінити їх значення за допомогою відображення та завантаження класу. Ви не повинні залежати від цього властивості для безпеки. Приклади див .: Чарівний трюк на Java


1
Я вірю, що такі хитрощі ви зможете робити лише в тому випадку, якщо ваш код працює з повною довірою, тому втрати безпеки немає. Ви також можете використовувати JNI для запису безпосередньо на місце пам'яті, де зберігаються рядки.
Антуан Обрі

Насправді я вважаю, що ви можете змінити будь-який незмінний предмет шляхом відображення.
Gqqnbig

0

Незмінність - це добре. Див. Ефективна Java. Якщо вам довелося копіювати рядок щоразу, коли ви передавали її навколо, тоді це було б багато схильного до помилок коду. Ви також маєте плутанину щодо того, які зміни впливають на посилання. Таким же чином, як Integer повинен бути незмінним, щоб поводитись як int, Strings повинен поводитися як непорушний, щоб діяти як примітиви. У C ++ передача рядків за значенням робить це без явної згадки у вихідному коді.


0

Існує виняток для майже кожного правила:

using System;
using System.Runtime.InteropServices;

namespace Guess
{
    class Program
    {
        static void Main(string[] args)
        {
            const string str = "ABC";

            Console.WriteLine(str);
            Console.WriteLine(str.GetHashCode());

            var handle = GCHandle.Alloc(str, GCHandleType.Pinned);

            try
            {
                Marshal.WriteInt16(handle.AddrOfPinnedObject(), 4, 'Z');

                Console.WriteLine(str);
                Console.WriteLine(str.GetHashCode());
            }
            finally
            {
                handle.Free();
            }
        }
    }
}

-1

Це значною мірою з міркувань безпеки. Набагато складніше захистити систему, якщо ви не можете довіряти, що ваші Strings захищені неправомірно.


1
Чи можете ви навести приклад того, що ви маєте на увазі під "несанкціонованим". Ця відповідь відчувається дійсно поза контекстом.
Гергелі Орош
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.