Непреложное недоразумение или ошибка в Документах? - PullRequest
5 голосов
/ 09 октября 2008

Я только что видел это в документах MS Visual Studio, и часть, выделенная жирным шрифтом, не имеет смысла для меня. Это неправильно или я не правильно понимаю? Если вы запустите это, b будет содержать «привет» (как я и ожидал), а не «h».

Строки неизменны - содержимое объекта строки не может быть изменено после создания объекта, хотя синтаксис заставляет его выглядеть так, как будто вы можете это сделать. Например, когда вы пишете этот код, компилятор фактически создает новый строковый объект для хранения новой последовательности символов, , а переменная b продолжает содержать «h» .

string b = "h";

b + = "ello";

Ответы [ 13 ]

7 голосов
/ 09 октября 2008

Вы сделали дополнение И задание за один шаг. Строки являются неизменяемыми, но также являются ссылочными типами.

string b = "h";
b = b + "ello";

Мы можем посмотреть на псевдопамять следующим образом:

string b = "h";         // b    := 0x00001000 ["h"]
string tmp1 = "ello";   // tmp1 := 0x00002000 ["ello"]
string tmp2 = b + tmp1; // tmp2 := 0x00003000 ["hello"]
string b = tmp2;        // b    := 0x00003000 ["hello"]

Я не совсем уверен, откуда вы получаете этот текст, потому что, читая документацию по классу string , я нахожу (не то, чтобы я думал, что "h" на самом деле собирает мусор):

Строки являются неизменными - содержимое строкового объекта не может быть изменено после создания объекта, хотя синтаксис заставляет его выглядеть так, как будто вы можете это сделать. Например, когда вы пишете этот код, компилятор фактически создает новый строковый объект для хранения новой последовательности символов, и этот новый объект назначается для b. Строка "h" тогда подходит для сборки мусора.

@ Джон Скит говорит, что «h» никогда не будет собирать мусор из-за интернирования строк, и я согласен с ним, но даже более того, с ним согласен стандарт C #, в противном случае следующее из стр. 2.4.4.5 литералов строк не может быть правдой:

Каждый строковый литерал не обязательно приводит к новому экземпляру строки. Когда два или более строковых литералов, которые эквивалентны в соответствии с оператором равенства строк (§7.9.7), появляются в одной и той же программе, эти строковые литералы ссылаются на один и тот же экземпляр строки.

5 голосов
/ 09 октября 2008

Люди, кажется, не понимают вопроса. Никто не утверждает, что строковые объекты не являются неизменяемыми. Суть спора в том, что он выдумал:

и переменная b продолжает удерживать "Ч"

Я согласен с ФП в том, что эта часть документа неверна по двум причинам:

(1) В очевидном интуитивном смысле, что если вы напечатаете (b) (или что-то еще в этом языке на правильном утверждении) после двух строк образца, вы получите «привет» в результате.
(2) В строгом смысле, переменная b не содержит"h", "hello" или любое строковое значение. Он содержит ссылку на строковый объект.

Содержимое переменной b изменяется в результате присваивания - оно изменяется от точки к строковому объекту "h" на указатель на строковый объект "привет".

Когда они говорят «держи», они на самом деле имеют в виду «указывает». И они не правы, после того, как присвоение b больше не указывает на «h».

Я думаю, что пример, который они действительно хотели привести, таков:

string a = "h";
string b = a;
b += "ello";

Дело в том, что я бы, по-моему, все еще указывал на "h"; то есть присвоение b не изменяет объект, на который он указывал, оно создает новый объект и изменяет b так, чтобы оно указывало на него.

(на самом деле я не пишу на C #, но это мое понимание.)

4 голосов
/ 09 октября 2008

Недоразумение здесь о ссылочных типах :
Строка является ссылочным типом , а не типом значения. Это означает, что ваша переменная b не является объектом типа string, это ссылка на объект типа string в памяти.
Документ говорит, что объект в памяти неизменен .
Тем не менее, ваша ссылка на объект может быть изменена для указания на какой-либо другой (неизменный) объект в памяти.
Для вас может показаться, что содержимое объекта изменилось, но в памяти это не изменилось, и это все, о чем неизменный вещь.

Сама строка является неизменной. То, что изменил ваш пример, это не строковый класс в памяти, а ссылка, на которую указывает ваша переменная.

См. Этот слегка измененный код:

string b = "h";
string m1 = b;
b += "ello";
// now b == "hello", m1 == "h"

В конце b будет указывать на "привет", а m1 будет указывать на "h". Для вас может показаться, что «h» изменился на «привет», но это не так. b + = "ello" создал новый класс строк, содержащий "hello", и назначил его для b, в то время как старый b все еще присутствует в памяти и все еще содержит "b".

Если бы строка не была неизменной, m1 также содержала бы «hello» вместо просто «b», потому что и b, и m1 указывали на одну и ту же ссылку.

4 голосов
/ 09 октября 2008

Документы неверны. Переменная b теперь содержит «привет». Строка является неизменной, но переменная может быть переназначена.

4 голосов
/ 09 октября 2008

Да, документы неверны. (Документы для ряда строковых методов также подразумевают изменчивость. Они в основном плохо написаны.)

Черт, даже использование "компилятора" для создания нового строкового объекта отключено. В основном это делает:

string b = "h";
b = string.Concat(b, "ello");

На этом этапе работа компилятора завершена - это структура, которая создает новый строковый объект.

2 голосов
/ 09 октября 2008

Строка не может быть изменена, но строковой переменной может быть присвоено другое значение. То, что вы делаете, ближе к:

string b = "h";
string temp = b + "ello";
b = temp;

Чтобы показать фактическую неизменность строки, это не удастся:

   string b="hello";
   if(b[0] == 'h')  // we can read via indexer
      b[0] = 'H';   // but this will fail.
1 голос
/ 09 октября 2008

Теперь есть три строки. Один - это оригинальное «h», один - «ello», а третий - «hello». Ваша переменная b указывает на строку "привет". Две другие строки не имеют ссылок на них и могут быть выброшены сборщиком мусора.

0 голосов
/ 28 июня 2012

Возможно, было бы яснее рассматривать все хранилища классового типа как содержащие «идентификаторы объектов». Предположим, что изначально компилятор присвоил ID #123 строке «h» и присвоил ID #547 строке «ello». Затем после оператора b = "h"; переменная b будет содержать ID #123. Оператор b += "ello"; заставит компилятор передать ID #123 и ID #547 оператору + для строки, который, в свою очередь, передаст их методу String.Concat. Этот метод, в свою очередь, попросит систему создать новый объект (например, ID #915) типа System.String, содержащий пять символов "hello", и вернуть этот объект вызывающей стороне. Затем компилятор сохранит ID #915 в b.

0 голосов
/ 09 октября 2008

Проще говоря, строки нельзя изменить на месте (если строка представляет собой массив символов)

0 голосов
/ 09 октября 2008

Попробуйте это:

string b = "h";
string c = b + "ello";    // b still == "h", c = "hello"
string d = string.concat(b, "ello"); // d == hello, b still "h"

Почему b все еще "h"? Поскольку «b» не является объектом, это объект ссылка . Вы ничего не можете сделать с объектом, на который ссылается b, чтобы изменить его. Если строки изменяемы, используйте:

string b = "ello";
string f = b.Insert("h",0);

изменит b на "привет" (потому что h был вставлен в позицию 0), но, поскольку он неизменен, b останется "ello".

Если вы измените ссылку на другой объект, это другая вещь.

b = "ello";
b = "Some other string";
// b not references "Some other string" , but the object "ello" remains unchanged.

Я надеюсь, что это помогает (и работает: S)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...