2009-02-17 24 views
13

He oído y leído que una cuerda no se puede cambiar (¿inmutable?). Eso debería ser correcto, supongo. Pero también he oído que dos cadenas con los mismos contenidos comparten el mismo espacio de memoria (o lo que usted llama). ¿Es esto correcto?C#: Cuerdas con los mismos contenidos

Y si es así, ¿eso significa que si creo una lista con miles de cadenas, realmente no ocuparía mucho espacio si la mayoría de esas cadenas fueran iguales entre sí?

Respuesta

18

EDIT: En la respuesta a continuación me he referido a la piscina pasante como dominio de aplicación específica; Estoy bastante seguro de que eso es lo que he observado antes, pero los documentos de MSDN para String.Intern sugieren que hay un solo grupo interno para todo el proceso, lo que lo hace aún más importante.

Respuesta original

(Iba a añadir esto como un comentario, pero yo creo que es un punto lo suficientemente importante como para necesitar una respuesta adicional ...)

Como otros han explicado, cuerda internación se produce para todos los literales de cadena, pero no en cadenas "creados de forma dinámica" (por ejemplo, los que se leen desde una base de datos o archivo, o construido utilizando StringBuilder o String.Format.)

sin embargo, yo no sugeriría c alling String.Intern para redondear el último punto: poblará el grupo interno durante la vida útil de su AppDomain. En su lugar, use un grupo que sea local solo a su uso.Aquí está un ejemplo de dicho pool:

public class StringPool 
{ 
    private readonly Dictionary<string,string> contents = 
     new Dictionary<string,string>(); 

    public string Add(string item) 
    { 
     string ret; 
     if (!contents.TryGetValue(item, out ret)) 
     { 
      contents[item] = item; 
      ret = item; 
     } 
     return ret; 
    } 
} 

Usted sería entonces sólo tiene que utilizar algo como:

string data = pool.Add(ReadItemFromDatabase()); 

(Tenga en cuenta que la piscina no es seguro para subprocesos; el uso normal no lo necesitaría para serlo)

De esta manera puede tirar su grupo tan pronto como ya no lo necesite, en lugar de tener un número potencialmente grande de cadenas en la memoria para siempre. También podría hacerlo más inteligente, implementando un caché LRU o algo así si realmente quisiera.

EDITAR: Solo para aclarar por qué es mejor que usar String.Intern ... supongamos que lee un montón de cadenas de una base de datos o archivo de registro, los procesa y luego pasa a otra tarea. Si llama al String.Intern en esas cadenas, serán nunca basura recolectada mientras su AppDomain esté vivo, y posiblemente ni siquiera entonces. Si carga varios archivos de registro diferentes, acumulará cadenas gradualmente en su grupo interno hasta que termine o se quede sin memoria. En cambio, estoy sugiriendo un patrón de esta manera:

void ProcessLogFile(string file) 
{ 
    StringPool pool = new StringPool(); 
    // Process the log file using strings in the pool 
} // The pool can now be garbage collected 

Aquí se obtiene el beneficio de las secuencias múltiples en el mismo archivo sólo una vez en la memoria existente (o, al menos, sólo para conseguir Gen0 pasado una vez), pero usted don' t contaminar un recurso "global" (el grupo interno).

+0

Jon, ¿podrías detallar qué obtienes al hacer esto? Supongo que ahora tendrá una función de comparación de cadenas de mayor rendimiento para cadenas en la agrupación. O me estoy perdiendo el punto aquí? –

+0

Editando la respuesta para explicar ... –

+1

oh, ¿las cadenas internas existen para siempre? Eso no es tan bueno, jeje. Gracias por notar eso. – Svish

6

Esto es más o menos cierto. Se llama "cadena de internados". Los literales de cadena estarán presentes en la memoria solo una vez y cada variable establecida en el mismo valor apunta a esta única representación. Sin embargo, las cadenas que se crean en código no se internan automáticamente.

http://msmvps.com/blogs/manoj/archive/2004/01/09/1549.aspx

+0

creado en el código? ¿no se crean todas las cadenas en el código? o ¿se refiere a cadenas codificadas, en lugar de ... es decir, cadenas extraídas de un tiempo de ejecución de la base de datos? – Svish

+0

Las cadenas creadas en código no se internan automáticamente, pero se pueden internar usando String.Intern(). Tenga en cuenta que existen algunas diferencias (¿fallas?) En cómo se maneja la cadena vacía para el internamiento en diferentes versiones de .NET: http://msdn.microsoft.com/en-us/library/system.string.intern.aspx?ppud = 4 –

+0

Entonces, cuando busco cadenas desde una base de datos, ¿tendría que usar String.Intern para que sea el caso? – Svish

0

Con el fin de hacer que las cadenas de "compartir" sus posiciones de memoria es a ellos pasante en la piscina interno, que contiene una sola referencia a cada cadena literal única declarada o creado mediante programación en su programa.

Tenga en cuenta que todos los literales de cadena en el código se internan automáticamente.

1

Si no recuerdo mal, las cadenas que están codificadas en código se agrupan por separado. Esto se llama "Internado" y existe un método para consultar si una cadena es: String.IsInterned Method

En esa página en "Observaciones" se puede leer:

El tiempo de ejecución de lenguaje común mantiene automáticamente una tabla, llamada el "grupo interno", que contiene una única instancia de cada constante de cadena literal única declarada en un programa, así como cualquier instancia única de Cadena que agregue mediante programación.

Espero que esto te ayude un poco, y corrígeme si me equivoco.

Matthias

Cuestiones relacionadas