¿Cuál es la diferencia entre una cadena mutable e inmutable en C #?

Respuestas:

313

Mutable e inmutable son palabras inglesas que significan "puede cambiar" y "no puede cambiar" respectivamente. El significado de las palabras es el mismo en el contexto de TI; es decir

  • se puede cambiar una cadena mutable y
  • una cadena inmutable no se puede cambiar.

El significado de estas palabras es el mismo en C # / .NET que en otros lenguajes / entornos de programación, aunque (obviamente) los nombres de los tipos pueden diferir, al igual que otros detalles.


Para el registro:

  • String es el tipo de cadena inmutable estándar C # / .Net
  • StringBuilder es el tipo de cadena mutable estándar C # / .Net

Para "efectuar un cambio" en una cadena representada como C # String, en realidad crea un nuevo Stringobjeto. El original Stringno se cambia ... porque no se puede cambiar.

En la mayoría de los casos es mejor usarlo Stringporque es una razón más fácil sobre ellos; por ejemplo, no necesita considerar la posibilidad de que algún otro hilo pueda "cambiar mi cadena". Sin embargo, cuando necesita construir o modificar una cadena usando una secuencia de operaciones, puede ser más eficiente usar a StringBuilder.


Y finalmente, para aquellas personas que afirman que a StringBuilderno es una cadena porque no es inmutable, la documentación de Microsoft describe StringBuilderlo siguiente:

"Representa una cadena mutable de caracteres. Esta clase no se puede heredar".

Stephen C
fuente
182

String es inmutable

es decir, las cadenas no pueden ser alteradas. Cuando modifica una cadena (al agregarla, por ejemplo), en realidad está creando una nueva cadena.

Pero StringBuilderno es inmutable (más bien, es mutable)

así que si tiene que alterar una cadena muchas veces, como múltiples concatenaciones, úsela StringBuilder.

Pankaj Agarwal
fuente
44
¿Qué sucede si concatenas cadenas inmutables muchas veces? ¿Un montón de uso de memoria para cadenas sin referencias? O simplemente lento?
Rudie
13
@Rudie - ambos. Cada concatenación crea un nuevo objeto String y copia todos los caracteres de ambas cadenas de entrada. Conduce al O(N^2)comportamiento ...
Stephen C
@StephenC explicado con las palabras clave.
Kent Liau
66
si tiene que modificar una cadena muchas veces, como múltiples concatenaciones, a continuación, utilizar StringBuilder ... que despejó mi mente totalmente gracias ...
katmanco
45

Un objeto es mutable si, una vez creado, su estado puede cambiarse llamando a varias operaciones en él, de lo contrario es inmutable.

Cadena inmutable

En C # (y .NET) una cadena está representada por la clase System.String. La stringpalabra clave es un alias para esta clase.

La clase System.String es inmutable, es decir, una vez creada, su estado no puede modificarse .

Así que todas las operaciones que realice en una cadena como Substring, Remove, Replace, concatenación usando el operador '+', etc va a crear una nueva cadena y lo devuelve.

Vea el siguiente programa para demostración:

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

Esto imprimirá 'string' y 'mystring' respectivamente.

Para obtener los beneficios de la inmutabilidad y por qué las cadenas son inmutables, compruebe ¿Por qué .NET String es inmutable? .

Cuerda mutable

Si desea tener una cadena que desea modificar a menudo, puede usar la StringBuilderclase. Las operaciones en una StringBuilder instancia modificarán el mismo objeto .

Para obtener más consejos sobre cuándo usar, StringBuilder consulte ¿ Cuándo usar StringBuilder? .

Unmesh Kondolikar
fuente
Hermosa explicación!
Hari
36

Todos los stringobjetos son inmutables en C #. Los objetos de la clase string, una vez creados, nunca pueden representar ningún valor que no sea con el que fueron construidos. Todas las operaciones que parecen "cambiar" una cadena en su lugar producen una nueva. Esto es ineficiente con la memoria, pero extremadamente útil con respecto a poder confiar en que una cadena no cambiará de forma debajo de usted, porque mientras no cambie su referencia, la cadena a la que se hace referencia nunca cambiará.

Un objeto mutable, por el contrario, tiene campos de datos que pueden modificarse. Uno o más de sus métodos cambiarán el contenido del objeto, o tiene una Propiedad que, cuando se escribe, cambiará el valor del objeto.

Si tiene un objeto mutable, el más similar a String es StringBuffer, entonces debe hacer una copia del mismo si desea estar absolutamente seguro de que no cambiará debajo de usted. Esta es la razón por la cual los objetos mutables son peligrosos para usar como claves en cualquier forma Dictionaryo conjunto: los objetos en sí mismos podrían cambiar, y la estructura de datos no tendría forma de saberlo, lo que llevaría a datos corruptos que eventualmente colapsarían su programa.

Sin embargo, puede cambiar su contenido, por lo que es mucho, mucho más eficiente en memoria que hacer una copia completa porque quería cambiar un solo carácter, o algo similar.

En general, lo correcto es usar objetos mutables mientras está creando algo, y objetos inmutables una vez que haya terminado. Esto se aplica a los objetos que tienen formas inmutables, por supuesto; La mayoría de las colecciones no. Sin embargo, a menudo es útil proporcionar formas de colecciones de solo lectura, que es el equivalente de inmutable, al enviar el estado interno de su colección a otros contextos; de lo contrario, algo podría tomar ese valor de retorno, hacerle algo y corromper su datos.

Adam Norberg
fuente
12

Inmutable:

Cuando realiza alguna operación en un objeto, crea un nuevo objeto, por lo tanto, el estado no es modificable como en el caso de una cadena.

Mudable

Cuando realiza alguna operación en un objeto, el objeto en sí no modifica ningún objeto nuevo creado como en el caso de StringBuilder

TalentTuner
fuente
8

En .NET System.String (también conocido como string) es un objeto inmutable. Eso significa que cuando creas un objeto no puedes cambiar su valor después. Solo puede recrear un objeto inmutable.

System.Text.StringBuilder es equivalente mutable de System.String y puede cambiar su valor

Por ejemplo:

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");
    }
}

Genera el siguiente MSIL: si investiga el código. Verá que cada vez que cambia un objeto de System.String, en realidad está creando uno nuevo. Pero en System.Text.StringBuilder cada vez que cambia el valor del texto no recrea el objeto.

.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
cahit beyaz
fuente
55
Ummm ... ese código desmontado solo muestra que se están realizando asignaciones de puntero y se están llamando métodos. No tiene (casi) nada que ver con mutable versus inmutable. (Se me escapa por qué algunas personas piensan que desarmar algún código de ejemplo irrelevante ayudará a las personas a comprender este tipo de cosas. Para empezar, la mayoría de los programadores no dominan el código CLI)
Stephen C
6

Las cadenas son mutables porque .NET usa un grupo de cadenas detrás de la escena. Significa :

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

Tanto name como name2 se refieren a la misma ubicación de memoria del conjunto de cadenas. Ahora suponga que desea cambiar el nombre2 a:

name2 = "My Loving Country";

Buscará en el grupo de cadenas la cadena "My Loving Country". Si lo encuentra, obtendrá la referencia del mismo. Se creará otra cadena nueva y sabia "My Loving Country" en el grupo de cadenas y name2 obtendrá la referencia. Pero todo este proceso " Mi país " no cambió porque otra variable como el nombre todavía lo está usando. Y esa es la razón por la cual las cadenas son INMUTABLES .

StringBuilder funciona de manera diferente y no utiliza un conjunto de cadenas. Cuando creamos cualquier instancia de StringBuilder:

var address  = new StringBuilder(500);

Asigna un fragmento de memoria de 500 bytes para esta instancia y todas las operaciones solo modifican esta ubicación de memoria y esta memoria no se comparte con ningún otro objeto. Y esa es la razón por la cual StringBuilder es MUTABLE .

Espero que sea de ayuda.

Rahul Garg
fuente
5

Ninguno, en realidad. La clase String es mutable.

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."
}

Este tipo de lógica se realiza todo el tiempo en las clases String y StringBuilder, en realidad. Simplemente asignan una nueva cadena cada vez que llama a Concat, Substring, etc. y usan aritmética de puntero para copiar a la nueva cadena. Las cadenas simplemente no se mutan, de ahí que se las considere "inmutables".


Por cierto, no intente esto con literales de cadena o dañará gravemente su programa:

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."

Esto se debe a que los literales de cadena están internados en el .NET Framework de escritorio; en otras palabras, bary bazseñalar exactamente la misma cadena, por lo que mutar una mutará a la otra. Sin embargo, todo esto está muy bien si estás usando una plataforma no administrada como WinRT, que carece de internados de cadenas.

James Ko
fuente
1
Bueno, sí. Si rompe las reglas al descifrar las abstracciones abiertas que se supone que están cerradas, entonces casi nada es inmutable. Puede hacer cosas análogas en Java utilizando una reflexión desagradable ... pero el JLS establece que el comportamiento que obtiene no está definido.
Stephen C
2

Para aclarar, no existe una cadena mutable en C # (o .NET en general). Otras langues admiten cadenas mutables (cadenas que pueden cambiar) pero el marco .NET no.

Entonces, la respuesta correcta a su pregunta es TODAS las cadenas son inmutables en C #.

cadena tiene un significado específico. La palabra clave "string" en minúsculas es simplemente un acceso directo para un objeto instanciado de la clase System.String. Todos los objetos creados a partir de la clase de cadena SIEMPRE son inmutables.

Si desea una representación de texto mutable, debe usar otra clase como StringBuilder. StringBuilder le permite construir iterativamente una colección de 'palabras' y luego convertirla en una cadena (una vez más inmutable).

Gerald Davis
fuente
Lo sentimos, pero la documentación de Microsoft StringBuildercontradice esto: ver msdn.microsoft.com/en-us/library/…
Stephen C
1

El valor de los datos no puede modificarse. Nota: El valor de la variable puede modificarse, pero el valor de datos inmutable original se descartó y se creó un nuevo valor de datos en la memoria.

vikas kumar
fuente
1

en detalle de implementación.

System.String de CLR2 es mutable. StringBuilder.Append llamando a String.AppendInplace (método privado)

System.String de CLR4 es inmutable. StringBuilder tiene una matriz Char con fragmentación.

kazuk
fuente
0

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

Respuesta corta: String es inmutable, mientras que StringBuilder es mutable.

Qué significa eso ? Wiki dice: En la orientación a objetos, un objeto inmutable es un objeto cuyo estado no puede modificarse después de su creación. Esto contrasta con un objeto mutable, que se puede modificar después de crearlo.

De la documentación de la clase StringBuilder:

El objeto String es inmutable. Cada vez que utiliza uno de los métodos en la clase System.String, crea un nuevo objeto de cadena en la memoria, que requiere una nueva asignación de espacio para ese nuevo objeto.

En situaciones donde necesita realizar modificaciones repetidas a una cadena, la sobrecarga asociada con la creación de un nuevo objeto de cadena puede ser costosa.

La clase System.Text.StringBuilder se puede usar cuando desea modificar una cadena sin crear un nuevo objeto. Por ejemplo, usar la clase StringBuilder puede aumentar el rendimiento al concatenar muchas cadenas juntas en un bucle.

Yasser Shaikh
fuente
0

Aquí está el ejemplo de cadena inmutable y generador de cadenas mutables

        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
fuente
Será muy bueno si también puede proporcionar la salida @Gokul :)
Roy Lee
La subcadena no cambia el valor subyacente. Solo devuelve un valor.
Kye
@Kye y por qué? porque las cuerdas son inmutables :)
LuckyLikey
Su código de 2 líneas: subcadena (0, 5); Console.WriteLine (s); Aquí s.substring () no cambia s. Este ejemplo no muestra si la cadena es inmutable.
Dhananjay
0

La cadena en C # es inmutable. Si lo concatena con cualquier cadena, en realidad está creando una nueva cadena, ¡ese es un nuevo objeto de cadena! Pero StringBuilder crea una cadena mutable.

MD Shahriar
fuente
0

StringBuilder es una mejor opción para concatenar una cadena de datos enorme porque StringBuilder es un tipo de cadena mutable y el objeto StringBuilder es un tipo inmutable, lo que significa que StringBuilder nunca crea una nueva instancia de objeto mientras concatena la cadena.

Si estamos usando una cadena en lugar de StringBuilder para lograr la concatenación, creará una nueva instancia en la memoria cada vez.

Sher Singh
fuente