Каква роля играе неизменността на низове в обяснението на препратките към низове на разработчиците?

Под една или друга форма често срещам следния въпрос (поставен тук в псевдокод):

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

Правя известно диагностично регистриране в събитието Page_Unload в приложение на asp.net, това регистриране може да отнеме доста време (около 100 ms). Ще бъде ли задържан потокът от отговори от кода в манипулатора за разтоварване на страница? Бих могъл да върша работата си асинхронно, като използвам theadpool, но предпочитам да не го правя, ако това няма да повлияе на времето за отговор на клиента.

Повече информация:

@thorkia е прав, тъй като в документацията се казва, че Page_Unload се извиква, след като отговорът бъде изпратен на клиента, но при моето тестване (както препоръча от @steve) той наистина блокира . Опитах Casini, IIS Express, Full IIS 7.5 (на тестов сървър) с компилации за освобождаване и отстраняване на грешки, със и без прикачен дебъгер. И, хващайки се за сламки, се опитах да сложа Async=true в Директивата за страницата. Опитах с Fiddler (поточното предаване е активирано) и без Fiddler. Пробвах с IE9 и Firefox. Ако документацията е „правилна“, тогава се чудя дали изпраща отговора, но може би не го „завършва“ (какво изобщо означава това, че ще трябва да проверя HTTP спецификацията) и така страницата не се изобразява в браузъра? Но моето разбиране беше, че браузърът на клиент започва да изобразява страницата, докато получава байтовете до това също няма смисъл за мен. Също така се опитах да разгледам кода в IL Spy, но мисля, че това може да ми отнеме много време.

Сега съм заинтригуван; правя ли нещо грешно или документацията е подвеждаща?

  -  person Gabe    schedule 03.07.2011
comment
@Gabe... Не доколкото знам. Но също така не съм запознат с езици с променливи низове (като тяхната реализация на „низ“ по подразбиране... 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
Итай, разбира се, но други обекти са променливи. Мога да модифицирам всякакви обекти в .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 = B; и B = C; не означава, че 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

Тук има няколко неща, които изглеждат грешни

  1. вие правите msg.d, но върнатият json обект няма свойство с име „d“.

  2. след като прехвърлите msg.d във вашата функция BuildTable, вие се опитвате да я използвате като масив.

  3. декларирате обект Person и след това се опитвате да посочите "person" с малки букви, това също е неправилно, тъй като js е чувствителен към главни и малки букви.

АКТУАЛИЗАЦИЯ:

function BuildTable(msg) {
        var person = jQuery.parseJSON( msg );
        var table = '<table><tr><td>' + person.firstName + '<td><tr></table>'
        $("#temp").html(table);
    };
- person Edwin Buck; 03.07.2011
comment
Не съм объркан относно това как работят низовете. Питам за ролята, която твърдението: Strings are Immutable играе във валиден отговор защо това е така. - 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 BTW за вашето мнение и за това, че отделихте време да отговорите на въпроса. - 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
@Steve, ако променливата съдържаше стойността като в действителния низ, тогава това е същото. Две различни променливи с една и съща стойност. Промяната на стойността на едното не променя стойността на другото. Независимо дали тази стойност е действителната стойност на обекта (като цяло число), или стойността на препратка, тя е една и съща. - 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 BTW за вашето мнение и за това, че отделихте време да отговорите на въпроса. - По отношение на вашия коментар: If you don't understand... да, разбирам напълно. Няма нужда от атаки. Просто подхождам към него от гледна точка на езиковия дизайн - и се опитвам да го видя от гледната точка на някой друг. Вие подхождате към него от гледна точка на начина, по който работи... и вече разбирам как работи. - person Steve; 03.07.2011