2009-07-04 11 views
7

A partir de una breve mirada con Reflector, parece que String.Substring() asigna memoria para cada subcadena. ¿Estoy en lo cierto de que este es el caso? Pensé que no sería necesario ya que las cadenas son inmutables.¿Por qué .NET crea nuevas subcadenas en lugar de señalar las cadenas existentes?

Mi objetivo subyacente era crear un método IEnumerable<string> Split(this String, Char) extensión que asigna ninguna memoria adicional.

+0

No lo he pensado muy bien, o miré la implementación de StringBuilder con Reflector, pero ¿funcionaría un método IEnumerable Split (este StringBuilder, Char)? – Domenic

+0

If String.Subcadena() no asigna nueva memoria, la cadena no será inmutable –

Respuesta

22

Una razón por la mayoría de los idiomas con cadenas inmutables crean nuevas subseries en lugar de referirse en cadenas existentes se debe a que esto interferirá con la basura recogida de las cadenas más tarde.

¿Qué sucede si se utiliza una cadena para su subcadena, pero entonces la cadena más grande se vuelve inalcanzable (excepto a través de la subcadena). La cadena más grande será incobrable, porque eso invalidaría la subcadena. Lo que parecía una buena manera de ahorrar memoria a corto plazo se convierte en una pérdida de memoria a largo plazo.

+1

Pensé que la razón principal estaba relacionada con los algoritmos sobre las cadenas. Si puede asumir con seguridad que una cadena nunca cambiará, puede pasar referencias a la misma de manera segura y también es intrínsecamente segura para el hilo. Supongo que eso también se relaciona con la recolección de basura. – Spence

+1

@Spence: esa es una razón para la inmutabilidad. No es una razón para evitar los búferes compartidos entre cadenas. Una vez que tiene inmutabilidad y GC, puede implementar fácilmente búferes compartidos detrás de escena sin romper la seguridad de los hilos o los algoritmos existentes. –

2

No es posible sin hurgar dentro de .net usando clases de String. Tendría que pasar referencias a una matriz que era mutable y asegurarse de que nadie se equivocara.

.Net creará una nueva cadena cada vez que se lo pida. La única excepción a esto son las cadenas internas creadas por el compilador (y usted puede hacerlo) que se colocan en la memoria una vez y luego se establecen punteros en la cadena por motivos de memoria y rendimiento.

0

Debido a que las cadenas son inmutables en .NET, cada operación de cadena que se traduce en un nuevo objeto de cadena asignará un nuevo bloque de memoria para los contenidos de la cadena.

En teoría, podría ser posible volver a utilizar la memoria cuando se extrae una subcadena, pero eso haría que la recolección de basura muy complicado: ¿y si se recoge la basura, la cadena original? ¿Qué pasaría con la subcadena que comparte una parte de ella?

Por supuesto, nada impide que el equipo .NET BCL para cambiar este comportamiento en futuras versiones de .NET. No tendría ningún impacto en el código existente.

+6

La cadena de Java realmente lo hace de esa manera: las subcadenas son simplemente punteros en la cadena original. Sin embargo, eso también significa que cuando toma una subcadena de 200 caracteres de una cadena de 200 MiB, la cadena de 200 MiB siempre estará en la memoria siempre que la subcadena pequeña no se recoja como basura. – Joey

+0

Creo que podría afectar el código existente dado que está diseñado en torno a este comportamiento. Si las personas suponen que internar su cadena evitará que se duplique y este comportamiento se detuvo, podría causar que las aplicaciones en funcionamiento se detengan sin excepciones de memoria. – Spence

+0

¿Cómo se puede diseñar en torno a este comportamiento? Debido a la inmutabilidad de las cadenas, realmente no hay forma de crear código que se rompa si cambia la implementación interna de la clase de cadena. –

1

Cada cadena tiene que tener su propio datos de cadena, con la forma en que la clase String se implementa.

Usted puede hacer su propia estructura subcadena que utiliza parte de una cadena:

public struct SubString { 

    private string _str; 
    private int _offset, _len; 

    public SubString(string str, int offset, int len) { 
     _str = str; 
     _offset = offset; 
     _len = len; 
    } 

    public int Length { get { return _len; } } 

    public char this[int index] { 
     get { 
     if (index < 0 || index > len) throw new IndexOutOfRangeException(); 
     return _str[_offset + index]; 
     } 
    } 

    public void WriteToStringBuilder(StringBuilder s) { 
     s.Write(_str, _offset, _len); 
    } 

    public override string ToString() { 
     return _str.Substring(_offset, _len); 
    } 

} 

Puede la carne hacia fuera con otros métodos como la comparación que también es posible hacerlo sin necesidad de extraer la cadena.

+0

¿Qué tal una subcadena en otra subcadena? –

+0

Sí, es fácil para la estructura SubString crear otra que sea parte de sí misma. – Guffa

0

Agregando al punto que las cadenas son inmutables, debe ser que el siguiente fragmento genere varias instancias de cadenas en la memoria.

String s1 = "Hello", s2 = ", ", s3 = "World!"; 
String res = s1 + s2 + s3; 

s1 + s2 => nueva instancia de cadena (temp1)

temp1 + s3 => nueva instancia de cadena (temp2)

res es una referencia a temp2.

+0

Esto suena como algo que la gente del compilador podría optimizar. –

+0

No es un problema con el compilador, es una elección hecha en el diseño del lenguaje. Java tiene las mismas reglas para Strings. System.Text.StringBuilder es una buena clase para usar que simula las cadenas "mutables". –

+1

Incorrecto - s1 + s2 + s3 se convierte en una sola llamada a String.Concat. Es por eso que NO es mejor usar String.Format o StringBuilder (que son comparativamente lentos), para hasta 4 cadenas. Mire la IL para ver qué hace el compilador, y use un generador de perfiles para averiguar qué funciona bien en su programa. De lo contrario, podrías estar diciendo: "¡Mira, es un zapato! ¡Se ha quitado el zapato y esta es una señal de que otros que lo seguirían deberían hacer lo mismo!" Por favor, publique respuestas basadas en hechos en lugar de respuestas míticas. –

Cuestiones relacionadas