Яка різниця між змінним і незмінним рядком в C #?


190

Яка різниця між змінним і незмінним рядком в C #?



6
Це не тотожно. Дуже схожий, але це не те саме, що ідентичне.
Марсело Кантос

2
Питання щодо внутрішніх справ StringBuilder stackoverflow.com/q/3564906/38206
Брайан Расмуссен

Відповіді:


313

Змінні та незмінні - це англійські слова, що означають "може змінюватися" та "не змінюється" відповідно. Значення слів однакове в контексті ІТ; тобто

  • змінна рядок може бути змінена, і
  • незмінний рядок неможливо змінити.

Значення цих слів однакові в C # / .NET, як і в інших мовах / середовищах програмування, хоча (очевидно) назви типів можуть відрізнятися, як і інші деталі.


Для запису:

  • String - це стандартний тип змінних рядків C # / .Net
  • StringBuilder - це стандартний тип змінного рядка C # / .Net

Щоб "здійснити зміну" на рядку, представленому як C # String, ви фактично створюєте новий Stringоб'єкт. Оригінал Stringне змінюється ... тому що він незмінний.

У більшості випадків це краще використовувати, Stringоскільки простіші причини про них; наприклад, вам не потрібно враховувати можливість того, що якийсь інший потік може "змінити мій рядок". Однак, коли вам потрібно побудувати або змінити рядок, використовуючи послідовність операцій, використання a може бути більш ефективним StringBuilder.


І нарешті, для тих людей, які стверджують, що a StringBuilderне є рядком, оскільки він не є незмінним, документація Microsoft описує StringBuilderтаким чином:

"Представляє змінний рядок символів. Цей клас не може бути успадкований."


182

String незмінна

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

Але StringBuilderне є незмінним (скоріше, він є змінним)

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


4
Що станеться, якщо ви багато разів скомпонуєте незмінні рядки? Навантаження пам'яті для рядків без посилань? Або просто повільно?
Rudie

13
@Rudie - і те, і інше. Кожна конкатенація створює новий об'єкт String і копіює всі символи обох вхідних рядків. Приводить до O(N^2)поведінки ...
Стівен C

@StephenC пояснив ключові слова.
Кент Ляу

6
якщо вам доведеться багато разів змінювати рядок, наприклад кілька конкатенацій, тоді використовуйте StringBuilder ... Це повністю очистило мій розум ...
katmanco

45

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

Незмінна струна

У C # (і .NET) рядок представлений класом System.String. ThestringКлючовим словом є псевдонімом для цього класу.

The System.String незмінний, тобто щойно створений стан не може бути змінений .

Отже всі операції, які ви виконуєте в рядку Substring,Remove ,Replace , конкатенації з допомогою оператора «+» і т.д. створить новий рядок і повернути його.

Дивіться наступну програму для демонстрації -

string str = "mystring";
string newString = str.Substring(2);
Console.WriteLine(newString);
Console.WriteLine(str);

Це надрукує "string" та "mystring" відповідно.

Для переваг непорушності та чому рядки є незмінними перевірити Чому .NET String є незмінним?.

Змінна струна

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

Для отримання додаткових порад про те, коли користуватися, StringBuilder див. Коли користуватися StringBuilder? .


Красиве пояснення!
Харі

36

Усі stringоб'єкти незмінні в C #. Щойно stringстворені об'єкти класу ніколи не можуть представляти будь-яку цінність, крім тієї, з якою вони були побудовані. Усі операції, які, здається, "змінюють" рядок, а не створюють нову. Це неефективно з пам’яттю, але надзвичайно корисно з огляду на те, що можна довіряти, що рядок не змінить форму під вами - тому що, поки ви не зміните свою посилання, рядок, на який буде посилатися, ніколи не зміниться.

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

Якщо у вас є об'єкт, що змінюється, найбільш схожий на String StringBuffer- тоді ви повинні зробити його копію, якщо ви хочете бути абсолютно впевненим, що він не зміниться з-під вас. Ось чому мінливі об'єкти небезпечно використовувати як ключі в будь-якій формі Dictionaryабо набір - самі об'єкти можуть змінюватися, і структура даних не могла б знати, що призведе до пошкодження даних, що в підсумку призведе до краху вашої програми.

Однак ви можете змінити його вміст, так що це набагато, набагато ефективніше пам’яті, ніж створення повної копії, оскільки ви хотіли змінити один символ або щось подібне.

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


12

Незмінне:

Коли ви робите якусь операцію над об'єктом, він створює новий об'єкт, отже, стан не піддається зміні, як у випадку рядка.

Змінні

Виконуючи якусь операцію над об'єктом, сам об'єкт не змінює жодного нового об'єкта, як у випадку StringBuilder


8

У .NET System.String (він же рядок) є незмінним об'єктом. Це означає, що коли ви створюєте об'єкт, ви не зможете згодом змінити його значення. Можна відтворити лише незмінний об’єкт.

System.Text.StringBuilder є змінним еквівалентом System.String, і ви можете змінити його значення

Наприклад:

class Program
{
    static void Main(string[] args)
    {

        System.String str = "inital value";
        str = "\nsecond value";
        str = "\nthird value";

        StringBuilder sb = new StringBuilder();
        sb.Append("initial value");
        sb.AppendLine("second value");
        sb.AppendLine("third value");
    }
}

Створює наступний MSIL: Якщо ви досліджуєте код. Ви побачите, що кожного разу, коли ви скануєте об'єкт System.String, ви фактично створюєте новий. Але в System.Text.StringBuilder, коли ви змінюєте значення тексту, ви не відтворюєте об'єкт.

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       62 (0x3e)
  .maxstack  2
  .locals init ([0] string str,
           [1] class [mscorlib]System.Text.StringBuilder sb)
  IL_0000:  nop
  IL_0001:  ldstr      "inital value"
  IL_0006:  stloc.0
  IL_0007:  ldstr      "\nsecond value"
  IL_000c:  stloc.0
  IL_000d:  ldstr      "\nthird value"
  IL_0012:  stloc.0
  IL_0013:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0018:  stloc.1
  IL_0019:  ldloc.1
  IL_001a:  ldstr      "initial value"
  IL_001f:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0024:  pop
  IL_0025:  ldloc.1
  IL_0026:  ldstr      "second value"
  IL_002b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_0030:  pop
  IL_0031:  ldloc.1
  IL_0032:  ldstr      "third value"
  IL_0037:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_003c:  pop
  IL_003d:  ret
} // end of method Program::Main

5
Уммм ... цей розібраний код показує лише, що виконуються призначення вказівників та викликаються методи. Це (майже) нічого спільного з незмінним та незмінним. (Мене уникає, чому деякі люди думають, що розбирання якогось невідповідного прикладу коду допоможе людям зрозуміти подібні речі. Для початку більшість програмістів не вільно володіє кодом CLI.)
Стівен C

6

Струни можна змінювати, оскільки .NET використовує рядок рядків за сценою. Це означає :

string name = "My Country";
string name2 = "My Country";

І ім’я, і ім'я2 посилаються на одне і те ж місце пам'яті з пулу рядків. Тепер припустимо, що ви хочете змінити name2 на:

name2 = "My Loving Country";

Він буде шукати пул рядків для рядка "My Loving Country", якщо його знайдете, ви отримаєте посилання на нього, інший мудрий новий рядок "My Loving Country" буде створений у ряді string та name2 отримає посилання на нього. Але це весь цей процес " Моя країна " не було змінено, оскільки інша змінна, як ім'я , все ще використовує її. І це є причиною того, що рядок НЕПРАВНИЙ .

StringBuilder працює по-різному і не використовує пуловий рядок. Коли ми створюємо будь-який примірник StringBuilder:

var address  = new StringBuilder(500);

Він виділяє шматок пам'яті розміром 500 байт для цього примірника, і всі операції просто змінюють це місце в пам'яті, і ця пам'ять не поділяється ні з одним іншим об'єктом. І це причина , чому StringBuilder є мутабельном .

Сподіваюся, це допоможе.


5

Ні, насправді. Клас String є змінним.

unsafe
{
    string foo = string.Copy("I am immutable.");
    fixed (char* pChar = foo)
    {
        char* pFoo = pChar;

        pFoo[5] = ' ';
        pFoo[6] = ' ';
    }

    Console.WriteLine(foo); // "I am   mutable."
}

Така логіка робиться постійно у класах String і StringBuilder. Вони просто виділяють нову рядок щоразу, коли ви викликаєте Concat, Substring тощо, і використовуєте арифметику вказівника для копіювання на новий рядок. Струни просто не мутують себе, тому їх вважають "непорушними".


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

string bar = "I am a string.";

fixed (char* pChar = bar)
{
    char* pBar = pChar;

    pBar[2] = ' ';
}

string baz = "I am a string.";

Console.WriteLine(baz); // "I  m a string."

Це тому, що рядкові літерали інтерновані в настільний .NET Framework; інакше кажучи, barі bazвкажіть на ту саму струну, тож мутування однієї мутуватиме іншу. Це все добре і просто, хоча якщо ви використовуєте некеровану платформу типу WinRT, якій не вистачає строкового інтернування.


1
Ну так. Якщо ви порушите правила, розбивши відкриті абстракції, які повинні бути закритими, то майже нічого не змінюється. Ви можете робити аналогічні речі на Java, використовуючи неприємні роздуми ... але JLS стверджує, що поведінка, яку ви отримуєте, не визначена.
Стівен С

2

Для уточнення немає такої речі, як змінна рядок у C # (або .NET взагалі). Інші мови підтримують змінні рядки (рядок, яка може змінюватися), але .NET Framework цього не робить.

Тож правильна відповідь на ваше запитання - ВСІ рядки незмінні в C #.

рядок має конкретне значення. Ключове слово рядка "рядка" - це лише ярлик для об'єкта, створеного з класу System.String. Усі об'єкти, створені з рядкового класу, ЗАВЖДИ незмінні.

Якщо ви хочете змінювати подання тексту, вам потрібно використовувати інший клас, наприклад StringBuilder. StringBuilder дозволяє ітеративно скласти колекцію "слів", а потім перетворити їх у рядок (знову незмінний).


Вибачте, але документація Microsoft StringBuilderсуперечить цьому: див. Msdn.microsoft.com/en-us/library/…
Стівен C

1

Значення даних може бути змінено. Примітка. Значення змінної може бути змінено, але вихідне значення незмінного даних було відкинуто і в пам'яті створено нове значення даних.


1

в деталях реалізації.

Система CLR2 System.String відключена. StringBuilder.Додати виклик String.AppendInplace (приватний метод)

Система CLR4 System.String незмінна. У StringBuilder є масив Char із фрагментом.


0

З http://yassershaikh.com/what-is-the-difference-bet between-strings-and-stringbuilder-in-c-net/

Короткий відповідь: Рядок незмінний - тоді як StringBuilder є змінним.

Що це означає ? Wiki каже: У об'єктно-орієнтованому незмінному об'єкті є об'єкт, стан якого не може бути змінений після його створення. Це на відміну від об'єкта, що змінюється, який можна змінити після його створення.

З документації до класу StringBuilder:

Об'єкт String незмінний. Кожен раз, коли ви використовуєте один із методів класу System.String, ви створюєте новий об'єкт в пам'яті, який вимагає нового виділення місця для цього нового об'єкта.

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

Клас System.Text.StringBuilder можна використовувати, коли потрібно змінити рядок без створення нового об'єкта. Наприклад, використання класу StringBuilder може підвищити продуктивність при об'єднанні багатьох струн разом у циклі.


0

Ось приклад для конструктора змінних рядків та змінних рядків

        Console.WriteLine("Mutable String Builder");
        Console.WriteLine("....................................");
        Console.WriteLine();
        StringBuilder sb = new StringBuilder("Very Good Morning");
        Console.WriteLine(sb.ToString());
        sb.Remove(0, 5);
        Console.WriteLine(sb.ToString());

        Console.WriteLine();

        Console.WriteLine("Immutable String");
        Console.WriteLine("....................................");
        Console.WriteLine();
        string s = "Very Good Morning";
        Console.WriteLine(s);
        s.Substring(0, 5);
        Console.WriteLine(s);
        Console.ReadLine();

Буде дуже приємно, якщо ви також можете надати вихід @Gokul :)
Рой Лі

Підрядка не змінює базове значення. Це лише повертає значення.
Kye

@Kye і чому? бо рядки незмінні :)
LuckyLikey

Ваш 2 рядовий код -: s.Substring (0, 5); Console.WriteLine (s); Тут s.substring () не змінюється s. цей приклад не показує, чи рядок незмінний.
Dhananjay

0

Рядок у C # незмінний. Якщо ви з'єднаєте його з будь-яким рядком, ви фактично створюєте нову рядок, тобто новий об'єкт рядка! Але StringBuilder створює змінний рядок.


0

StringBuilder є кращим варіантом для скручування величезних рядків даних, тому що StringBuilder - це тип, що змінюється, а об'єкт StringBuilder - незмінний тип, що означає, що StringBuilder ніколи не створює новий екземпляр об'єкта під час скручування рядка.

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

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