2009-04-22 22 views
5

Mi pregunta es la siguiente: ¿La concatenación de cadenas en C# es segura? Si la concatenación de cadenas conduce a errores inesperados, y el reemplazo de esa cadena de concatenación mediante StringBuilder hace que esos errores desaparezcan, ¿qué podría indicar eso?Concatenación de cadenas insegura en C#, ¿necesita usar StringBuilder?

Antecedentes: Estoy desarrollando una aplicación de línea de comando pequeña C#. Toma argumentos de línea de comandos, realiza una consulta SQL ligeramente complicada y genera aproximadamente 1300 filas de datos en un archivo XML formateado.

Mi programa inicial siempre funcionaría bien en modo de depuración. Sin embargo, en el modo de lanzamiento, llegaría aproximadamente al 750º resultado de SQL y luego moriría con un error. El error fue que una cierta columna de datos no se podía leer, incluso a través del método Read() del objeto SqlDataReader que acaba de devolver verdadero.

Este problema se solucionó al usar StringBuilder para todas las operaciones en el código, donde anteriormente había habido "cadena1 + cadena2". No estoy hablando de concatenación de cadenas dentro del bucle de consulta SQL, donde StringBuilder ya estaba en uso. Estoy hablando de concatenaciones simples entre dos o tres variables cortas de cadena anteriores en el código.

Tuve la impresión de que C# era lo suficientemente inteligente como para manejar la gestión de memoria y agregar algunas cadenas. ¿Me equivoco? ¿O esto indica algún otro tipo de problema de código?

+0

¿Podría pegar el fragmento de código con la concatenación de cadenas frente a la versión de StringBuilder? La única razón por la que podría pensar que se puede obtener una diferencia con la concatenación de cadenas frente a StringBuilder es que se llama a la sobrecarga de algunas cosas, pero eso no debería tener un efecto como el que está describiendo. –

+0

Código original: string filepath = ruta + raíz de archivo + ".xml"; Código actualizado: cadena filepath = new StringBuilder (ruta) .Append (fileroot) .Append (".xml"). ToString(); Hice cambios como ese en varios lugares. Todos estos son antes del ciclo principal. Dentro del ciclo principal, siempre he estado usando StringBuilder para crear el contenido del archivo XML. (No estoy usando XML API porque fue más rápido, y se supone que es un programa rápido y sucio) –

Respuesta

7

Aparte de lo que estás haciendo, es mejor hacer las API XML en lugar de strings o StringBuilder. Dudo que el error que ves se deba a la concatenación de cadenas. Tal vez cambiar a StringBuilder simplemente enmascaró el error o lo pasó con gracia, pero dudo que usar cadenas realmente sea la causa.

+0

Gracias a todos por tus respuestas Todavía terminé viendo errores para ciertos conjuntos de datos después de todos mis cambios. Creo que la verdadera raíz del problema estaba en la conexión SQL, y que "cambiar a StringBuilder simplemente enmascaró el error", como dijo Johannes. Resolví el problema haciendo uso de una clase de contenedor SQL de otro proyecto. Esta clase convierte todo el conjunto de resultados de SQL en un objeto de diccionario, por lo que no es necesario mantener el conjunto de resultados y la conexión SQL abiertos. –

11

La concatenación de cadenas es más segura, aunque requiere más memoria que el uso de StringBuilder si contacta un gran número de cadenas en un bucle. Y en casos extremos, puede que se esté quedando sin memoria.

Es casi seguro que es un error en su código.

Quizás esté contactando una gran cantidad de cadenas. O tal vez es algo completamente diferente.

Volvería a la depuración sin ideas preconcebidas sobre la causa raíz. Si aún tiene problemas intente reducirlo al mínimo necesario para reprogramar el problema y el código postal.

-3

Al combinar cadenas, siempre utilizo StringBuilder. Está diseñado para eso y es más eficiente que simplemente usar "string1 + string2".

+3

Un mal consejo. string1 + string2 es más rápido que StringBuilder en muchas situaciones: StringBuilder gana al hacer una gran cantidad de concatenaciones en un bucle. – Joe

+0

Gracias por avisarme. Había leído que era más rápido en "C# vía CLR", pero si ese no es el caso, es bueno saberlo. – patjbs

+0

Acabo de ejecutar una serie de pruebas e incluso concatenar dos cadenas cortas a través de "str1 + str2" IS es más lento, o en el mejor de los casos, no es más rápido que utilizar StringBuilder. Y varios artículos excelentes del blog que he leído tienden a estar de acuerdo con esa conclusión. Si bien utilizar str1 + str2 puede ser una práctica conveniencia y no tiene un impacto significativo en la concatenación de cuerdas pequeñas, StringBuilder no es peor, y ganará a medida que crezca el número de concats (tan pequeño como 4 la diferencia es sig) – patjbs

3

¿Cuánto tiempo tomaría la versión de concatenación frente a la versión del generador de cadenas? Es posible que su conexión a la base de datos esté siendo cerrada. Si está haciendo mucha concatenación, iría con StringBuilder ya que es un poco más eficiente.

1

Una causa puede ser que las cadenas son inmutables en .Net por lo que cuando se realiza una operación en una como la concatenación, en realidad se está creando una nueva cadena.

Otra posible causa es que la longitud de la cadena es un int, por lo que la longitud máxima posible es Int32.MaxValue o 2.147.483.647.

En cualquier caso, un StringBuilder es mejor que "string1 + string2" para este tipo de operación. Aunque, el uso de las capacidades XML integradas sería aún mejor.

0

Aquí es mi tiro en la oscuridad ...

cadenas en .NET (no stringbuilders) entrar en la cadena Intern piscina. Esto es básicamente un área administrada por el CLR para compartir cadenas para mejorar el rendimiento. Tiene que haber un límite aquí, aunque no tengo idea de cuál es ese límite. Me imagino que toda la concatenación que estás haciendo está golpeando el techo del grupo interno de cuerdas. Entonces, SQL dice que sí, tengo un valor para usted, pero no puede colocarlo en ninguna parte, por lo que obtiene una excepción.

Una prueba rápida y fácil sería nGen su conjunto y vea si todavía obtiene el error. Después de nGen'ing, su aplicación ya no usará el grupo.

Si eso falla, me pondría en contacto con Microsoft para tratar de obtener algunos detalles difíciles. Creo que mi idea suena plausible, pero no tengo idea de por qué funciona en modo de depuración. Tal vez en el modo de depuración las cadenas no están internados. Yo tampoco soy un experto.

13

Para responder a su pregunta: contatenation cadena en C# (y .NET en general) es "seguro", pero hacerlo en un bucle estrecho que usted describe es probable que cause presión severa de la memoria y poner la tensión en el recolector de basura.

Me atrevería a suponer que los errores de los que habla estaban relacionados con el agotamiento de recursos de algún tipo, pero sería útil si pudiera proporcionar más detalles, por ejemplo, ¿recibió una excepción? ¿La aplicación terminó anormalmente?

Antecedentes: .NET las cadenas son inmutables, por lo que cuando se hace una concatenación de esta manera:

var stringList = new List<string> {"aaa", "bbb", "ccc", "ddd", //... }; 
string result = String.Empty; 
foreach (var s in stringList) 
{ 
    result = result + s; 
} 

Esto es aproximadamente equivalente a la siguiente:

string result = ""; 
result = "aaa" 
string temp1 = result + "bbb"; 
result = temp1; 
string temp2 = temp1 + "ccc"; 
result = temp2; 
string temp3 = temp2 + "ddd"; 
result = temp3; 
// ... 
result = tempN + x; 

El propósito de esta Por ejemplo, enfatizar que cada vez que se pasa el bucle resulta en la asignación de una nueva cadena temporal.

Dado que las cadenas son inmutables, el tiempo de ejecución no tiene más opciones que asignar una nueva cadena cada vez que agrega otra cadena al final de su resultado.

Aunque la cadena result se actualiza constantemente para apuntar al último y mayor resultado intermedio, está produciendo una gran cantidad de estas cadenas temporales sin nombre que se vuelven aptas para la recolección de basura casi de inmediato.

Al final de esta concatenación, tendrá las siguientes cadenas almacenadas en la memoria (asumiendo, por simplicidad, que el recolector de basura no se ha ejecutado todavía).

string a = "aaa"; 
string b = "bbb"; 
string c = "ccc"; 
// ... 
string temp1 = "aaabbb"; 
string temp2 = "aaabbbccc"; 
string temp3 = "aaabbbcccddd"; 
string temp4 = "aaabbbcccdddeee"; 
string temp5 = "aaabbbcccdddeeefff"; 
string temp6 = "aaabbbcccdddeeefffggg"; 
// ... 

Aunque todas estas variables temporales implícitas son elegibles para la recolección de basura casi de inmediato, todavía tienen que asignarse. Al realizar la concatenación en un ciclo cerrado, esto pondrá mucha tensión en el recolector de basura y, si nada más, hará que su código se ejecute muy lentamente. He visto el impacto en el rendimiento de esta primera mano, y se vuelve realmente dramático a medida que su secuencia concatenada se hace más grande.

Lo que se recomienda es utilizar siempre una StringBuilder si se está haciendo más que unas pocas concatenaciones de cadenas.StringBuilder usa un buffer mutable para reducir el número de asignaciones que son necesarias para construir su cadena.

+0

Excelente respuesta :) – patjbs

+4

Estoy bastante seguro de que a + b + c + d ... en una sola declaración no genera las cadenas intermedias. Funciona más bien como String.Concat. – Joe

+0

Joe: Tienes razón. Fui demasiado lejos al simplificar la respuesta. He agregado un bucle para que sea más correcto desde el punto de vista de los hechos ahora. ¿Eso está mejor? –

0

string.Concat(string[]) es, con mucho, la forma más rápida de concatenar cadenas. Literalmente mata StringBuilder en rendimiento cuando se usa en bucles, especialmente si crea el StringBuilder en cada iteración. Hay muchas referencias si busca en Google "C# string format vs stringbuilder" o algo así. http://www.codeproject.com/KB/cs/StringBuilder_vs_String.aspx te da una idea sobre los tiempos. Aquí string.Join gana la prueba de concatenación pero creo que esto se debe a que se usa string.Concat(string, string) en lugar de la versión sobrecargada que toma una matriz. Si echa un vistazo al código MSIL que se genera con los diferentes métodos, verá lo que ocurre debajo del capó.

Cuestiones relacionadas