Какую роль играет неизменяемость строк в объяснении ссылок на строки для разработчиков?

В той или иной форме я часто сталкиваюсь со следующим вопросом (заданным здесь в псевдокоде):

String myString = "Hello"
someObject.stringProperty = myString
myString = "World"

Почему someObject.stringProperty теперь не равно «World»?

Кажется, существует путаница в отношении роли, которую играет следующее утверждение в объяснении того, почему это так:

Строки неизменны

Что вы думаете?

Если вы считаете, что это утверждение неприменимо, я бы спросил вас вот о чем: в языке, где строки были изменяемыми, а оператор присваивания изменял их фактическое значение (вместо простого изменения ссылки), ваш ответ все еще имеет смысл?

ИЗМЕНИТЬ:

Хорошо, я чувствую необходимость прояснить некоторые вещи:

  1. Меня не смущает, как работают строки, ссылки или присваивания. Я совершенно ясно говорю об этой теме. Я не спрашиваю, как работают струны. Я спрашиваю: «Какую роль играет неизменяемость строк в объяснении ссылок на строки для разработчиков». Мы можем пропустить атаки Ad-Hominem, которые меня должны смущать.

  2. Я ищу логически строгий ответ для разработчика, задающего процитированный вопрос, который не содержит или не предполагает неизменности строк.

Классификация существующих аргументов:

  1. String Immutability has nothing to do with it because the references are changing, not the values of the strings Этот ответ предполагает именно тот факт, о котором я спрашиваю.

  2. Assignment means assignment of references not assignment of values Опять же, это предполагает именно тот факт, о котором я спрашиваю. Нет причин, по которым это должно иметь место для строк. Это просто имеется случай со строками для повышения производительности и других причин.


person Steve    schedule 03.07.2011    source источник
comment
Стив, кажется, вы путаете что-то очень важное для языков, с которыми вы работаете. Оператор присваивания ('=') не изменяет значение любого объекта (неизменяемого или нет); скорее, он устанавливает значение сохраненного указателя на объект в памяти. Независимо от изменчивости объекта использование оператора присваивания для изменения значения указателя не изменит и не может изменить значение объекта. По какой-то причине это кажется вам перепутанным (возможно, вас неправильно учили), но это очень просто так.   -  person Itai Ferber    schedule 03.07.2011
comment
Кроме того, в вашем гипотетическом мире, если бы оператор присваивания вел себя таким образом, характер вопроса изменился бы, и, конечно, ответ был бы другим. Но это просто так не работает, и вы сами об этом говорите в вопросе.   -  person Itai Ferber    schedule 03.07.2011
comment
There is no reason it _must_ be the case for strings. Это относится не только к строкам, так ведет себя оператор присваивания в C# для всех типов.   -  person Rob    schedule 03.07.2011
comment
@Роб, так? Это не так в C++ (где вы можете переопределить оператор присваивания). Если бы разработчики языка C# решили, что не хотят, чтобы строки работали в языке таким образом, они могли бы это сделать.   -  person Steve    schedule 03.07.2011
comment
@Steve: Верно, но когда вы переопределяете оператор присваивания, вы можете существенно «сломать» ожидаемую функциональность оператора. На самом деле это не имеет ничего общего с неизменностью. Это происходит и с изменяемыми объектами.   -  person Rob    schedule 03.07.2011
comment
Роб, ваш аргумент that is how the assignment operator behaves in C# for all types, но вы не признаете, что строки уже имеют специальную синтаксическую обработку в C#... просто посмотрите, как они создаются - так что мне трудно понять, почему это не воспринимается как язык выбор дизайна. Ответ, который я продолжаю получать от людей, кажется, потому что так оно и есть. Да, я понимаю, что так оно и есть... не в этом вопрос.   -  person Steve    schedule 03.07.2011
comment
Есть ли какой-либо язык, в котором использование оператора присваивания для строковой переменной изменяет содержимое этой переменной, а не изменяет то, на что она указывает?   -  person Gabe    schedule 03.07.2011
comment
@ Гейб ... Не то, чтобы я знал об этом. Но я также не знаю ни одного языка с изменяемыми строками (в качестве их реализации «строки» по умолчанию... NSMutableString/MutableString здесь не учитываются, потому что они не имеют того же специального синтаксиса, что и встроенная строка)   -  person Steve    schedule 03.07.2011
comment
Стив, для начала, никто на тебя не нападает. Называть эти атаки ad hominem несколько излишне. А во-вторых, трудно понять, чего именно вы добиваетесь. Вы говорите, что вам ясно, как работают ссылки (и у меня нет больше причин не верить этому), но если это так, то о чем мы спорим? Вы возражаете против того, что «это просто так», но это только так. Больше обсуждать особо нечего. Очевидно, что если бы все было по-другому, в соответствии с вашим вопросом, наши ответы были бы другими. Но это не так.   -  person Itai Ferber    schedule 03.07.2011
comment
Кроме того, поскольку вы, кажется, увлекаетесь логическими ошибками, вы должны полностью понимать, что нет смысла обсуждать обоснованность любого другого случая, кроме того, что в настоящее время является технической проблемой, поскольку это само по себе является ошибкой. Ваш вопрос, кажется, зависит от «ну, а что, если это не так», но этот вопрос спорен, потому что а) это так и б) нет смысла обсуждать какие-либо другие возможности, потому что из ложного утверждения можно сделать вывод любой вывод они хотят. Если бы оператор присваивания работал по-другому, могло бы произойти что угодно, но это не так.   -  person Itai Ferber    schedule 03.07.2011
comment
@Итай. Спасибо. Я смущен, почему люди не рассматривают это как выбор дизайна языка. Кто-то мог внедрить строки для работы альтернативным способом, но не сделал этого (по "очевидным" причинам, которые вы и я понимаем). Но это был выбор. И тот «выбор», который был сделан, на мой взгляд, соответствует определению «неизменяемого»… и никто не предложил ни одного аргумента, который бы в какой-то момент не делал этого предположения о цепочках — которое для кого-то кто не знает - думаю, все же нужно пояснить.   -  person Steve    schedule 03.07.2011
comment
Это действительно суть моего вопроса. Можете ли вы объяснить, почему строки работают так, как они работают, в какой-то момент, не говоря ни слова, потому что фактическое значение, на которое указывает ссылка, никогда не может измениться, и язык применяет это ограничение. Я не думаю, что вы можете, и не слышал, чтобы кто-нибудь дал ответ, указывающий на обратное.   -  person Steve    schedule 03.07.2011
comment
there is no point in discussing the validity of any other case but what is currently the technical issue at hand - Суть в назидании. Более глубокое понимание того, что есть, чего нет и почему это так, является фундаментальным аспектом роста. Понять, что это решение языкового дизайна, и понять, почему это решение имеет такой большой смысл, что каждый язык (о котором я знаю) работает таким образом, но что существует также и мир языкового дизайна, в котором вы можете создавать свои собственные языковые правила и поиск компромиссов — важная часть понимания того, что происходит на самом деле. ИМО.   -  person Steve    schedule 03.07.2011
comment
Вот ваш ответ. Нет, вы не можете именно потому, что язык накладывает это ограничение. В этом весь смысл. Если бы по какой-либо причине оператор присваивания работал иначе только для строк, это привело бы к несоответствиям. Причина, по которой неизменность не играет роли в присваивании строк, заключается в том, что она не играет никакой роли в присваивании объектов; строки — это объекты, и случай строки — это всего лишь частный случай объекта. Если бы вы выбрали этот дизайн по-другому, то, конечно, все работало бы по-другому. Но их просто нет. Это более полно отвечает на ваш вопрос?   -  person Itai Ferber    schedule 03.07.2011
comment
Вы только что сказали, что это не играет никакой роли, потому что это так. ??   -  person Steve    schedule 03.07.2011
comment
Короче говоря, дело в согласованности: во многих языках оператор присваивания, ссылки и указатели работают одинаково для строк и для объектов, потому что строки являются объектами. Это выбор дизайна, основанный на стремлении к простоте и согласованности, и реализация только строк для работы по-разному — это просто боль в заднице. Поэтому некоторые дизайнерские решения были выбраны таким образом, и по этой причине «это просто так». Это ключевой момент здесь. Вы хотите, чтобы я написал это как ответ, а не просто комментарий?   -  person Itai Ferber    schedule 03.07.2011
comment
Itai, Конечно, но другие объекты изменяемы. Я могу модифицировать все виды объектов в .NET, используя свойства, методы или прямой доступ к ivar для изменения значений их переменных экземпляра. Однако я не могу изменить второй символ в строке. Я также думаю, что ваше обоснование выбора неверно (This is a design choice rooted in the quest for simplicity and consistency). Во всяком случае, это потому, что вы не можете выделить достаточно памяти для неопределенного количества символов, и любая сложная структура данных была бы слишком неэффективной для базового типа, такого как нить.   -  person Steve    schedule 03.07.2011
comment
@Steve, позвольте нам продолжить это обсуждение в чате   -  person Itai Ferber    schedule 03.07.2011


Ответы (3)


Роль, которую играет утверждение в объяснении, зависит от самого объяснения. Это может быть вредным или полезным, в зависимости от остальной части объяснения.

Лично я бы не стал использовать это утверждение до довольно поздних объяснений (по крайней мере, в наши дни). Неизменяемость строк просто несколько мешает - как это происходит с объяснениями передачи параметров. Я бы начал с объяснения, используя изменяемый класс, например:

House x = new House(Color.Green); // The has a green front door
House y = x;
x = new House(Color.Red);
Console.WriteLine(y.FrontDoorColor); // Still green!

Здесь я бы объяснил, что x и y это как бумажки с адресами домов. Присваивание во второй строке не копирует дом — копирует адрес дома. Присваивание в третьей строке не меняет цвет входной двери в первом доме — оно создает новый дом, а затем стирает адрес на первой части бумаги (x) и пишет новый адрес. Это ничего не меняет ни в первом доме, ни во втором листе бумаги (y).

Затем я бы создал второй пример:

House x = new House(Color.Green); // The has a green front door
House y = x;
x.FrontDoorColor = Color.Red; // Repainting a front door
Console.WriteLine(y.FrontDoorColor); // Red!

На этот раз есть только один дом — если я покрашу дверь дома, и вы придете посмотреть на нее с адресом, который я дал вам ранее, вы увидите, что входная дверь теперь красная.

Все идет нормально. Теперь я мог бы вернуться к исходному примеру и сказать, что он уже выглядит как первый фрагмент дома, а не как второй, поэтому ведет себя точно так же. Затем я могу сказать, что неизменяемость строк означает, что вы даже не можете написать код, похожий на второй пример дома, но использующий строки. Таким образом, неизменяемость строк не была бы сразу актуальной для объяснения существующего кода, но все равно появилась бы в том же ответе.

(Я также должен указать, что, хотя ссылки ведут себя как адреса реального мира, я не утверждаю, что они такие же, как «адреса памяти» или указатели. Они не должны быть. Я использую этот термин в строгая аналогия с реальным миром, и все. Это минус примера, но я не думаю, что это сильно вредит.)

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

Чтобы узнать, будет ли мой ответ актуален в языке с изменяемыми строками, нам нужно больше узнать о том, как ведут себя строковые литералы. Язык, который был бы таким же, как C# во всех отношениях, за исключением изменчивости строк, был бы ужасным языком, как вы могли бы написать:

// Hypothetical bad language
string x = "dog";
x.MutateTo("cat");
Console.WriteLine("dog"); // Prints cat!

Это явно нежелательно, поэтому, по-видимому, поведение строковых литералов должно измениться. Вы также говорите о возможных изменениях значения оператора присваивания и т. д. Трудно делать конкретные заявления о гипотетическом языке, не зная точно, как он себя ведет.

person Jon Skeet    schedule 03.07.2011

Это связано с тем, что знак равенства в компьютерном программировании является скорее оператором присваивания, чем условием «математического равенства». То, что строка неизменяема, не имеет к этому никакого отношения. Все дело в том, что знак равенства является оператором «присваивания», а не ограничением математической эквивалентности.

Это означает, что А = В; и В = С; не означает, что A = C;

вместо этого это означает

A было установлено, чтобы ссылаться на значение B, поэтому значение B теперь является значением A. B был установлен для ссылки на значение C, поэтому значение C теперь является значением B, но значение A остается неизменным.

Если бы строки не были неизменяемыми

String myString = "Hello";
myString.replace(3, "p"); // replace starting at char #3 (the second 'l')
System.out.println(myString); // would print "Help"

Но поскольку строки неизменяемы;

String myString = "Hello";
myString.replace(3, "p"); // returns a new string "help" which is not assigned to anything
// since the newly returned string was not assigned to anything, it was garbage collected
System.out.println(myString); // would print "Hello"
person Edwin Buck    schedule 03.07.2011
comment
Стив, в своем последнем задании вы меняете ссылку myString на ссылку World, используя знак равенства. Таким образом, это не повлияет на то, что уже произошло ранее, когда someObject.stringProperty было установлено на предыдущее значение myString. Знак равенства является оператором присваивания, а не оператором математической эквивалентности. - person Edwin Buck; 03.07.2011
comment
Меня не смущает, как работают струны. Я спрашиваю о роли, которую играет утверждение: строки неизменны в правильном ответе на вопрос, почему это так. - person Steve; 03.07.2011
comment
В данном случае это не играет роли, потому что неизменность строк даже не требуется для того, на что вы смотрите. Неизменяемость строк связана с изменением значения строки после ее создания, а в приведенном выше примере этого никогда не происходит, он просто присваивает одни и те же неизменяемые строки паре разных переменных. Я думаю, вы путаете неизменность с константой. Неизменяемый означает, что значение не может быть изменено, а постоянный означает, что ссылка не может быть переназначена. - person Edwin Buck; 03.07.2011
comment
Вы утверждаете, что это не играет никакой роли, потому что это так. Приведенный выше пример не делает этого только потому, что строки неизменяемы. en.wikipedia.org/wiki/Begging_the_question - person Steve; 03.07.2011
comment
Я добавил пример, который будет иметь два разных результата в зависимости от неизменности. Вы увидите, что назначение не имеет к этому никакого отношения. - person Edwin Buck; 03.07.2011
comment
You'll see that assignment doesn't have anything to do with it. Только потому, что ты решил, что этого не должно быть. Не могли бы вы привести пример с присваиванием? - person Steve; 03.07.2011
comment
Теперь в вашем альтернативном мире, где оператор присваивания фактически изменяет значение, ваш код не будет работать; потому что у вас нет оператора присваивания, только оператор изменения значения. - person Edwin Buck; 03.07.2011
comment
+1 Кстати, за ваше мнение и за то, что нашли время ответить на вопрос. - person Steve; 03.07.2011

Объяснение в том, что этот пример не имеет ничего общего с неизменностью.

У вас есть две переменные, первоначально содержащие одно и то же значение, что для строки означает, что они указывают на один и тот же объект в памяти. Затем вы меняете значение одной из переменных и указываете его на другой объект в памяти.

Это ничем не отличается от того, чтобы сказать

int a = 4;
int b = a;
a = 5; 

Or

string x = "Foo";
string y = x;
x = "Bar";

Or

Foo foo = new Foo() { Bar = 42 };
Foo otherFoo = foo;
foo = new Foo() { Bar = 17; }

Переменные на мгновение ссылаются на одно и то же. В остальном они неразрывно связаны на все времена. Как только вы указываете одному на что-то другое, их общность заканчивается.

person Anthony Pegram    schedule 03.07.2011
comment
Вы не обратили внимание на последний абзац. - person Steve; 03.07.2011
comment
@ Стив, если переменная содержит значение, как в реальной строке, то это то же самое. Две разные переменные, содержащие одно и то же значение. Изменение значения одного не меняет значения другого. Независимо от того, является ли это значение фактическим значением объекта (например, целым числом) или значением ссылки, это одно и то же. - person Anthony Pegram; 03.07.2011
comment
Если бы внутри языка/системы все строки были реализованы в виде связанных списков символов (вместо неизменяемых массивов с завершающим нулем), а ссылка на головной узел всегда оставалась неизменной, так что значение строки могло бы быть изменено .... тогда..... ? - person Steve; 03.07.2011
comment
Что ты спрашиваешь? В гипотетическом мире, где правила были другими, все по определению могло быть другим. В таком гипотетическом мире вы могли бы изменять строки и сохранять несколько переменных, каждая из которых содержала бы одно и то же значение, даже после перезаписи одной из них. У вас могут быть целые числа, строки и Foos, связанные на все времена. Если вы измените правила, все возможно. Однако в мире C# и в представленном примере факт остается фактом: неизменность не имеет никакого отношения к объяснению. - person Anthony Pegram; 03.07.2011
comment
Но в мире, где кто-то не понимает, ВОЗМОЖНО все. Ваши утверждения имеют смысл только потому, что (по замыслу) строки были реализованы как неизменяемые объекты. Это проблема, на которую я пытаюсь ответить. Вы не можете привести свой аргумент, не предполагая утверждение неизменности. - person Steve; 03.07.2011
comment
Боюсь, вы просто не правы. Если вы не понимаете идею о том, что у вас есть две независимые переменные и изменение одной не повлияет на другую, вы просто проиграли. Неизменяемость не имеет к этому никакого отношения, и я определенно имею в виду ничего. - person Anthony Pegram; 03.07.2011
comment
+1 Кстати, за ваше мнение и за то, что нашли время ответить на вопрос. - Что касается вашего комментария: If you don't understand... да, я вас прекрасно понимаю. Не надо атак. Я просто подхожу к этому с точки зрения дизайна языка — и я пытаюсь увидеть это с точки зрения кого-то другого. Вы подходите к этому с прямой точки зрения, вот как это работает... и я уже понимаю, как это работает. - person Steve; 03.07.2011