viernes, 6 de junio de 2008

Cadenas en .NET


Las cadenas del tipo System.String en .NET son inmutables. Esto quiere decir que cualquier cambio a una cadena provoca que el entorno de ejecución cree una nueva cadena y abandone la vieja. Esto es invisible, y muchos programadores se podrán asombrar al aprender que el siguiente código almacena cuatro nuevas cadenas en la memoria:


string s;

s = "Uno"; // "Uno"
s += " Dos"; // "Uno Dos"
s += " Tres"; // "Uno Dos Tres"
s += " Cuatro"; // "Uno Dos Tres Cuatro"
Console.WriteLine(s);

Sólo la última cadena tiene una referencia, las demás serán desechadas en el proceso de recolección de basura. Evitar este tipo de cadenas temporales ayuda a evitar recolección de basura innecesaria, lo que mejora la performance. Existen varias maneras de evitar las cadenas temporales:

  • Usar los métodos Concat, Join, o Format de la clase String para unir múltiples elementos en una sola declaración.

  • Usar la clase StringBuilder para crear cadenas dinámicas (mutables).


El uso de la clase StringBuilder es la solución más flexible. El siguiente código muesta el uso de la clase StringBuilder:


System.Text.StringBuilder sb = new System.Text.StringBuilder(30);
sb.Append("Uno"); // Construir la cadena
sb.Append(" Dos");
sb.Append(" Tres");
sb.Append(" Cuatro");
string s = sb.ToString(); // Copiar el resultado en la cadena
Console.WriteLine(s);

4 comentarios:

Federico Medrano dijo...


Muy buena publicación Guillermo.

Ezequiel dijo...

Hola, ¿que tal?

Soy programador Java pero me interesó este tema...

¿En que medida varía el rendimiento al reemplazar un String por un StringBuilder?

Es decir, ¿es significativa la diferencia?

Creo que se debería analizar el funcionamiento interno...
Por ejemplo, si codifico esto en java:

s = "Uno" + " Dos" + " Tres" + " Cuatro";

internamente (en la JVM) se trabaja como esto:

s = new StringBuilder("Uno").append(" Dos").append(" Tres").append(" Cuatro").toString();

Guillermo Bellmann dijo...

Bien, en .NET no sucede lo que vos comentás que sucede en la JavaVM. Simplemente se "descarta" la cadena que ya no se utiliza y queda en la memoria hasta que el recolector de basura (Garbage Collector) la elimina. (Para entender como funciona esto, leé el post "Memoria y Performance" que hizo Julio.)
Entonces, en el primer ejemplo, quedaría la cadena s = "Uno Dos Tres Cuatro" como válida. Y las cadenas "Uno", "Uno Dos" y "Uno Dos Tres" quedarían en la memoria sin poder ser utilizadas y esperando a ser borradas por el GC, pero ocupando espacio hasta que esto suceda.

Julio dijo...

El StringBuilder en realidad es una lista enlazada de punteros a cadenas, de ese modo es que podemos agregar todo lo que se nos de la gana sin dejar basura en el medio.
Por eso es que al final simpre tenemos que invoar el método ToString() que nos entrega una sola cadena de todo lo que hayamos hecho.

Por lo que Ezequiel comenta del funcionamiento interno del JVM, me animo a decir que .NET hace lo mismo siempre y cuando usemos en una misma asignación la secuencia de:

s = "uno" + "dos" + "tres" + "cuatro"

Esta asignación es resuelta por el compilador (medianto alguna técnica de optimización de código) sin desperdiciar nada, sin embargo si hacemos varias asignaciones es cuando perdemos las referencias y queda trabajo para el Garbage Collector.