В чем разница между изменяемой и неизменной строкой в ​​C #?

190

В чем разница между изменяемой и неизменной строкой в ​​C #?

Venkadesh
источник
2
возможный дубликат - stackoverflow.com/questions/2365272/why-net-string-is-immutable
RameshVel
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
Что произойдет, если вы объедините неизменные строки много раз? Нагрузка использования памяти для строк без ссылок? Или просто медленно?
Руди
13
@Rudie - оба. Каждая конкатенация создает новый объект String и копирует все символы обеих входных строк. Приводит к O(N^2)поведению ...
Стивен С.
@StephenC объяснил с ключевыми словами.
Кент Ляу
6
если вам приходится многократно изменять строку, например, несколько конкатенаций, тогда используйте StringBuilder ... Это полностью очистило мой разум, спасибо ...
katmanco
45

Объект является изменяемым, если однажды созданный объект может быть изменен путем вызова различных операций с ним, в противном случае он является неизменным.

Неизменная Строка

В C # (и .NET) строка представлена ​​классом System.String. stringКлючевым словом является псевдонимом для этого класса.

Класс System.String является неизменным, т.е. после создания его состояние не может быть изменено .

Таким образом , все операции вы выполняете на строке , как Substring, Remove, Replace, конкатенации с помощью оператора «+» и т.д. создаст новую строку и вернуть его.

Смотрите следующую программу для демонстрации -

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

Это выведет «string» и «mystring» соответственно.

Для преимуществ неизменности и почему строка неизменна проверка Почему .NET Строка неизмененна? ,

Mutable String

Если вы хотите иметь строку, которую хотите часто изменять, вы можете использовать StringBuilderкласс. Операции над StringBuilder экземпляром изменят тот же объект .

Для получения более подробной консультации по когда использовать StringBuilder смотрите Когда использовать StringBuilder? ,

Унмеш Кондоликар
источник
Прекрасное объяснение!
Хари
36

Все stringобъекты неизменны в C #. stringСозданные объекты класса никогда не могут представлять ничего, кроме того, с которым они были созданы. Все операции, которые, кажется, «изменяют» строку, вместо этого производят новую. Это неэффективно с памятью, но чрезвычайно полезно для уверенности в том, что строка не изменит форму под вами - потому что, пока вы не измените свою ссылку, строка, на которую вы ссылаетесь, никогда не изменится.

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

Если у вас есть изменяемый объект, наиболее похожий на String, StringBufferвы должны сделать его копию, если хотите быть абсолютно уверенным, что он не изменится из-под вас. Вот почему изменяемые объекты опасны для использования в качестве ключей в любой форме Dictionaryили для установки - сами объекты могут измениться, и структура данных не сможет узнать, что приведет к повреждению данных, что в конечном итоге приведет к сбою вашей программы.

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

Как правило, правильнее всего использовать изменяемые объекты при создании чего-либо и неизменные объекты после завершения работы. Конечно, это относится к объектам, которые имеют неизменные формы; большинство коллекций этого не делают. Часто бывает полезно предоставлять формы коллекций только для чтения, что эквивалентно неизменяемости при отправке внутреннего состояния вашей коллекции в другие контексты - в противном случае что-то может принять это возвращаемое значение, что-то с ним сделать и испортить вашу данные.

Адам Норберг
источник
12

Неизменный :

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

изменчивый

Когда вы выполняете какую-либо операцию над объектом, сам объект не изменял созданный новый объект, как в случае StringBuilder

TalentTuner
источник
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.)
Стивен С.
6

Строки являются изменяемыми, потому что .NET используют пул строк за сценой. Это значит :

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

И имя, и имя2 ссылаются на одну и ту же область памяти из пула строк. Теперь предположим, что вы хотите изменить name2 на:

name2 = "My Loving Country";

Он будет искать в строковом пуле строку «Моя любящая страна», если найден, вы получите ссылку на нее, в мудром пуле будет создана новая мудрая строка «Моя любящая страна», а name2 получит ссылку на нее. Но весь этот процесс « Моя страна » не изменился, потому что другая переменная, такая как name , все еще использует его. И это причина, почему строки являются НЕМЕРТНЫМИ .

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 - нет.

Таким образом, правильный ответ на ваш вопрос - ВСЕ строки неизменны в C #.

Строка имеет особое значение. Строковое ключевое слово "string" - это просто ярлык для объекта, созданного из класса System.String. Все объекты, созданные из строкового класса, ВСЕГДА неизменны.

Если вам нужно изменяемое представление текста, вам нужно использовать другой класс, например StringBuilder. StringBuilder позволяет итеративно создавать коллекцию «слов», а затем преобразовывать ее в строку (снова неизменяемую).

Джеральд Дэвис
источник
Извините, но документация Microsoft StringBuilderпротиворечит этому: см. Msdn.microsoft.com/en-us/library/…
Стивен С.
1

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

Викас Кумар
источник
1

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

System.String в CLR2 является изменяемой. StringBuilder.Append, вызывающий String.AppendInplace (закрытый метод)

System.String в CLR4 является неизменным. StringBuilder имеет массив Char с чанкингом.

kazuk
источник
0

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

Краткий ответ: String является неизменным - тогда как StringBuilder является изменяемым.

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

Из документации класса 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
источник
Было бы очень хорошо, если бы вы могли предоставить вывод @Gokul :)
Рой Ли
Подстрока не меняет базовое значение. Он только возвращает значение.
Кай
@ А почему? потому что строки неизменяемы :)
LuckyLikey
Ваш двухстрочный код -: s.Substring (0, 5); ЕЫпе (ы); Здесь s.substring () не меняет s. этот пример не показывает, является ли строка неизменной.
Дхананджай
0

Строка в C # неизменна. Если вы объединяете его с какой-либо строкой, вы фактически создаете новую строку, то есть новый строковый объект! Но StringBuilder создает изменяемую строку.

Д-р Шахриар
источник
0

StringBuilder - лучший вариант для конкатенации огромной строки данных, поскольку StringBuilder является изменяемым строковым типом, а объект StringBuilder является неизменяемым типом, то есть StringBuilder никогда не создает новый экземпляр объекта во время конкататации строки.

Если мы используем строку вместо StringBuilder для достижения конкатенации, то он будет каждый раз создавать новый экземпляр в памяти.

Шер Сингх
источник