Salida de cadena: formato o concat en C #?

Digamos que desea dar salida o concat cadenas. ¿Cuál de los siguientes estilos prefiere?

  • var p = new { FirstName = "Bill", LastName = "Gates" };

  • Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

  • Console.WriteLine(p.FirstName + " " + p.LastName);

¿Prefiere usar formato o simplemente concat cuerdas? ¿Cual es tu favorito? ¿Te está lastimando uno de estos ojos?

¿Tiene algún argumento racional para usar uno y no el otro?

Yo iría por el segundo.

Prueba este código

Es una versión ligeramente modificada de tu código.
1. Eliminé Console.WriteLine ya que probablemente sean unos pocos órdenes de magnitud más lentos de lo que bash medir.
2. Estoy iniciando el cronómetro antes del ciclo y deteniéndolo justo después, de esta manera no pierdo precisión si la función requiere, por ejemplo, 26.4 tics para ejecutarse.
3. La forma en que dividiste el resultado por algunas iteraciones fue incorrecta. Vea lo que sucede si tiene 1000 milisegundos y 100 milisegundos. En ambas situaciones, obtendrá 0 ms después de dividirlo por 1000000.

 Stopwatch s = new Stopwatch(); var p = new { FirstName = "Bill", LastName = "Gates" }; int n = 1000000; long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0; string result; s.Start(); for (var i = 0; i < n; i++) result = (p.FirstName + " " + p.LastName); s.Stop(); cElapsedMilliseconds = s.ElapsedMilliseconds; cElapsedTicks = s.ElapsedTicks; s.Reset(); s.Start(); for (var i = 0; i < n; i++) result = string.Format("{0} {1}", p.FirstName, p.LastName); s.Stop(); fElapsedMilliseconds = s.ElapsedMilliseconds; fElapsedTicks = s.ElapsedTicks; s.Reset(); Console.Clear(); Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks"); Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks"); Thread.Sleep(4000); 

Esos son mis resultados:

1000000 x result = string.Format ("{0} {1}", p.FirstName, p.LastName); tomó: 618ms - 2213706 tics
1000000 x resultado = (p.FirstName + "" + p.LastName); tomó: 166 ms - 595610 tics

Me sorprende que tanta gente quiera encontrar inmediatamente el código que ejecuta más rápido. Si UN MILLÓN de iteraciones TODAVÍA tarda menos de un segundo en procesarse, ¿esto será CUALQUIER MANERA notable para el usuario final? No muy probable.

Optimización prematura = FAIL.

Me gustaría ir con la opción String.Format , solo porque tiene más sentido desde el punto de vista arquitectónico. No me importa el rendimiento hasta que se convierta en un problema (y si lo hiciera, me preguntaría: ¿necesito concatenar un millón de nombres a la vez? Seguramente no todos encajarán en la pantalla …)

Considere si su cliente más tarde desea cambiarlo para que pueda configurar si se mostrará "Firstname Lastname" o "Lastname, Firstname." Con la opción Formato, esto es fácil: simplemente cambie la cadena de formato. Con el concat, necesitarás código adicional. Claro que eso no suena como un gran problema en este ejemplo en particular, pero extrapolar.

Cariño, después de leer una de las otras respuestas, traté de invertir el orden de las operaciones, por lo que primero realizo la concatenación, luego el archivo String.Format …

 Bill Gates Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks Bill Gates Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks 

Entonces, el orden de las operaciones hace una gran diferencia, o mejor dicho, la primera operación SIEMPRE es mucho más lenta.

Aquí están los resultados de una corrida donde las operaciones se completan más de una vez. He intentado cambiar los pedidos, pero las cosas generalmente siguen las mismas reglas, una vez que se ignora el primer resultado:

 Bill Gates Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks Bill Gates Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks Bill Gates Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks Bill Gates Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks Bill Gates Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks Bill Gates String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks Bill Gates String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks 

Como puede ver, las ejecuciones posteriores del mismo método (refactoré el código en 3 métodos) son incrementalmente más rápidas. El más rápido parece ser el método Console.WriteLine (String.Concat (…)), seguido de una concatenación normal y luego las operaciones formateadas.

El retraso inicial en el inicio probablemente sea la inicialización de Console Stream, ya que al colocar Console.Writeline (“Start!”) Antes de la primera operación, todos los tiempos vuelven a estar en línea.

Las cadenas son inmutables, esto significa que la misma pequeña porción de memoria se usa una y otra vez en su código. Agregar las mismas dos cadenas juntas y crear la misma nueva cadena una y otra vez no afecta la memoria. .Net es lo suficientemente inteligente como para usar la misma referencia de memoria. Por lo tanto, su código realmente no prueba la diferencia entre los dos métodos concat.

Prueba esto para el tamaño:

 Stopwatch s = new Stopwatch(); int n = 1000000; long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0; Random random = new Random(DateTime.Now.Millisecond); string result; s.Start(); for (var i = 0; i < n; i++) result = (random.Next().ToString() + " " + random.Next().ToString()); s.Stop(); cElapsedMilliseconds = s.ElapsedMilliseconds; cElapsedTicks = s.ElapsedTicks; s.Reset(); s.Start(); for (var i = 0; i < n; i++) result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString()); s.Stop(); fElapsedMilliseconds = s.ElapsedMilliseconds; fElapsedTicks = s.ElapsedTicks; s.Reset(); StringBuilder sb = new StringBuilder(); s.Start(); for(var i = 0; i < n; i++){ sb.Clear(); sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); } s.Stop(); sbElapsedMilliseconds = s.ElapsedMilliseconds; sbElapsedTicks = s.ElapsedTicks; s.Reset(); Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks"); Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks"); Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks"); Console.WriteLine("****************"); Console.WriteLine("Press Enter to Quit"); Console.ReadLine(); 

Muestra de salida:

 1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks 1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks 1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks 

Lástima los pobres traductores

Si sabe que su aplicación permanecerá en inglés, entonces bien, guarde las marcas del reloj. Sin embargo, muchas culturas generalmente verían Apellido Nombre, por ejemplo, direcciones.

Así que use string.Format() , especialmente si alguna vez va a tener su aplicación en string.Format() lugar donde el inglés no sea el primer idioma.

Aquí están mis resultados más de 100.000 iteraciones:

 Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks 

Y aquí está el código de banco:

 Stopwatch s = new Stopwatch(); var p = new { FirstName = "Bill", LastName = "Gates" }; //First print to remove the initial cost Console.WriteLine(p.FirstName + " " + p.LastName); Console.WriteLine("{0} {1}", p.FirstName, p.LastName); int n = 100000; long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0; for (var i = 0; i < n; i++) { s.Start(); Console.WriteLine(p.FirstName + " " + p.LastName); s.Stop(); cElapsedMilliseconds += s.ElapsedMilliseconds; cElapsedTicks += s.ElapsedTicks; s.Reset(); s.Start(); Console.WriteLine("{0} {1}", p.FirstName, p.LastName); s.Stop(); fElapsedMilliseconds += s.ElapsedMilliseconds; fElapsedTicks += s.ElapsedTicks; s.Reset(); } Console.Clear(); Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks"); Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks"); 

Entonces, no sé de quién es la respuesta para marcar como respuesta 🙂

La concatenación de cadenas está bien en un escenario simple como ese: es más complicado con algo más complicado que eso, incluso Apellido, Nombre. Con el formato puede ver, de un vistazo, cuál será la estructura final de la cadena al leer el código, con la concatenación se vuelve casi imposible discernir inmediatamente el resultado final (excepto con un ejemplo muy simple como este).

Lo que eso significa a largo plazo es que cuando vuelvas para hacer un cambio en el formato de tu cadena, tendrás la capacidad de abrir y hacer algunos ajustes en la cadena de formato, o arrugar la frente y comenzar a moverte alrededor de todo tipos de acceso a propiedades mezclados con texto, que es más probable que introduzca problemas.

Si está usando .NET 3.5, puede usar un método de extensión como este y obtener una syntax fácil de usar, como esta:

 string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4); 

Finalmente, a medida que su aplicación crezca en complejidad, puede decidir que para mantener cuerdas de manera sana en su aplicación, desea moverlas a un archivo de recursos para localizarlas o simplemente a un ayudante estático. Esto será MUCHO más fácil de lograr si ha utilizado formatos consistentemente, y su código puede ser simplemente refactorizado para usar algo como

 string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName); 

Para una manipulación muy simple, utilizaría la concatenación, pero una vez que superas los 2 o 3 elementos, el formato se vuelve más apropiado para la OMI.

Otra razón para preferir String.Format es que las cadenas .NET son inmutables y hacerlo de esta manera crea menos copias temporales / intermedias.

Si bien entiendo totalmente la preferencia de estilo y seleccioné la concatenación para mi primera respuesta, en parte en función de mis propias preferencias, parte de mi decisión se basó en el pensamiento de que la concatenación sería más rápida. Entonces, por curiosidad, lo probé y los resultados fueron asombrosos, especialmente para una cuerda tan pequeña.

Usando el siguiente código:

  System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch(); var p = new { FirstName = "Bill", LastName = "Gates" }; s.Start(); Console.WriteLine("{0} {1}", p.FirstName, p.LastName); s.Stop(); Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks"); s.Reset(); s.Start(); Console.WriteLine(p.FirstName + " " + p.LastName); s.Stop(); Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks"); 

Obtuve los siguientes resultados:

 Bill Gates Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks Bill Gates Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks 

Usar el método de formateo es más de 100 veces más lento. La concatenación ni siquiera se registró como 1 ms, por lo que también elaboré los tics del temporizador.

Para la concatenación básica de cadenas, generalmente utilizo el segundo estilo, más fácil de leer y más simple. Sin embargo, si estoy haciendo una combinación de cadenas más complicada, suelo optar por String.Format.

String.Format ahorra en muchas cotizaciones y ventajas …

 Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp); vs Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + "."); 

Solo se guardaron unos pocos charicters, pero creo que, en este ejemplo, el formato lo hace mucho más limpio.

Una mejor prueba sería observar su memoria usando Perfmon y los contadores de memoria CLR. Según tengo entendido, la razón por la que desea utilizar String.Format en lugar de simplemente concatenar cadenas es que, como las cadenas son inmutables, está cargando innecesariamente al recolector de elementos no utilizados con cadenas temporales que deben recuperarse en la siguiente pasada.

StringBuilder y String.Format, aunque son potencialmente más lentos, son más eficientes en cuanto a la memoria.

¿Qué tiene de malo la concatenación de cadenas?

En general, prefiero el primero, ya que especialmente cuando las cuerdas se alargan puede ser mucho más fácil de leer.

El otro beneficio es uno de rendimiento, ya que este último realmente realiza 2 declaraciones de creación de cadena antes de pasar la cadena final al método Console.Write. String.Format utiliza un StringBuilder bajo las cubiertas, creo, por lo que se evitan múltiples concatenaciones.

Sin embargo, debe tenerse en cuenta que si los parámetros que está pasando a String.Format (y otros métodos como Console.Write) son tipos de valor, se incluirán en cuadros antes de que se pasen, lo que puede proporcionar sus propios resultados de rendimiento. Publicación de blog sobre esto aquí .

A partir de C # 6.0 cadenas interpoladas se puede utilizar para hacer esto, lo que simplifica aún más el formato.

 var name = "Bill"; var surname = "Gates"; MessageBox.Show($"Welcome to the show, {name} {surname}!"); 

Una expresión de cadena interpolada se parece a una cadena de plantilla que contiene expresiones. Una expresión de cadena interpolada crea una cadena reemplazando las expresiones contenidas por las representaciones ToString de los resultados de las expresiones.

Las cadenas interpoladas tienen un rendimiento similar a String.Format, pero una legibilidad mejorada y una syntax más corta, debido al hecho de que los valores y las expresiones se insertan en línea.

Consulte también este artículo de dotnetperls sobre la interpolación de cadenas.

Si está buscando una forma predeterminada para formatear sus cadenas, esto tiene sentido en términos de legibilidad y rendimiento (excepto si microsegundos va a hacer una diferencia en su caso de uso específico).

  1. El formateo es la forma de hacerlo “.NET”. Ciertas herramientas de refactorización (¡Refactor! Para uno) incluso proponen refactorizar el código de estilo concat para usar el estilo de formateo.
  2. El formateo es más fácil de optimizar para el comstackdor (aunque el segundo probablemente será refactorizado para usar el método ‘Concat’, que es rápido).
  3. El formateo suele ser más claro para leer (especialmente con el formato “sofisticado”).
  4. El formateo significa llamadas implícitas a ‘.ToString’ en todas las variables, lo que es bueno para la legibilidad.
  5. De acuerdo con “Effective C #”, las implementaciones .NET ‘WriteLine’ y ‘Format’ están en mal estado, autobox todos los tipos de valor (que es malo). “Effective C #” aconseja realizar llamadas ‘.ToString’ explícitamente, lo que en mi humilde opinión es falso (consulte la publicación de Jeff )
  6. Por el momento, el comstackdor no verifica las sugerencias de tipo de formato, lo que da como resultado errores en el tiempo de ejecución. Sin embargo, esto podría ser modificado en futuras versiones.

Elijo basado en la legibilidad. Prefiero la opción de formato cuando hay algo de texto alrededor de las variables. En este ejemplo:

 Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp); 

entiendes el significado incluso sin nombres variables, mientras que el concat se llena de comillas y signos + y confunde mis ojos:

 Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + "."); 

(Tomé prestado el ejemplo de Mike porque me gusta)

Si la cadena de formato no significa mucho sin nombres de variables, tengo que usar concat:

  Console.WriteLine("{0} {1}", p.FirstName, p.LastName); 

La opción de formato me hace leer los nombres de las variables y asignarlos a los números correspondientes. La opción concat no requiere eso. Todavía estoy confundido por las comillas y los signos +, pero la alternativa es peor. ¿Rubí?

  Console.WriteLine(p.FirstName + " " + p.LastName); 

En cuanto al rendimiento, espero que la opción de formato sea más lenta que el concat, ya que el formato requiere que se analice la cadena. No recuerdo haber tenido que optimizar este tipo de instrucción, pero si lo hiciera, buscaría métodos de string como Concat() y Join() .

La otra ventaja del formato es que la cadena de formato se puede poner en un archivo de configuración. Muy útil con los mensajes de error y el texto de la interfaz de usuario.

Si tiene la intención de localizar el resultado, entonces String.Format es esencial porque es posible que los diferentes lenguajes naturales ni siquiera tengan los datos en el mismo orden.

Usaría String.Format, pero también tendría la cadena de formato en los archivos de recursos para que se pueda localizar en otros idiomas. Usar un string concat simple no te permite hacer eso. Obviamente, si alguna vez necesita localizar esa cadena, esta no es una razón para pensar. Realmente depende de para qué es la cadena.

Si se va a mostrar al usuario, usaría String.Format para poder localizarlo si fuera necesario, y FxCop lo corregirá por mí, por si acaso 🙂

Si contiene números u otras cosas que no sean cadenas (por ejemplo, fechas), utilizaría String.Format porque me da más control sobre el formateo .

Si es para crear una consulta como SQL, usaría Linq .

Si para concatenar cadenas dentro de un bucle, usaría StringBuilder para evitar problemas de rendimiento.

Si es por alguna salida que el usuario no verá, y no va a afectar el rendimiento, usaría String.Format porque tengo el hábito de usarlo de todos modos y estoy acostumbrado 🙂

Dentro de una semana a partir del 19 de agosto de 2015, esta pregunta tendrá exactamente siete (7) años. Ahora hay una mejor manera de hacer esto. Mejor en términos de capacidad de mantenimiento ya que no he realizado ninguna prueba de rendimiento en comparación con solo concatenar cadenas (¿pero importa estos días? ¿Unos pocos milisegundos de diferencia?). La nueva forma de hacerlo con C # 6.0 :

 var p = new { FirstName = "Bill", LastName = "Gates" }; var fullname = $"{p.FirstName} {p.LastName}"; 

Esta nueva característica es mejor , IMO, y en realidad es mejor en nuestro caso, ya que tenemos códigos en los que construimos querystrings cuyos valores dependen de algunos factores. Imagine una cadena de consulta donde tenemos 6 argumentos. Entonces, en lugar de hacer una, por ejemplo:

 var qs = string.Format("q1={0}&q2={1}&q3={2}&q4={3}&q5={4}&q6={5}", someVar, anotherVarWithLongName, var3, var4, var5, var6) 

en puede escribirse así y es más fácil de leer:

 var qs=$"q1={someVar}&q2={anotherVarWithLongName}&q3={var3}&q4={var4}&q5={var5}&q6={var6}"; 

Si está tratando con algo que debe ser fácil de leer (y esto es más código), me quedaría con la versión de sobrecarga del operador A MENOS QUE:

  • El código debe ser ejecutado millones de veces
  • Estás haciendo toneladas de concats (más de 4 es una tonelada)
  • El código está dirigido hacia el Marco Compacto

En al menos dos de estas circunstancias, usaría StringBuilder en su lugar.

Creo que esto depende en gran medida de cuán compleja es la salida. Tiendo a elegir el escenario que funcione mejor en ese momento.

Elija la herramienta adecuada según el trabajo: D ¡Lo que parece más limpio!

Prefiero el segundo también, pero no tengo argumentos racionales en este momento para apoyar esa posición.

¡Buena esa!

Recien agregado

  s.Start(); for (var i = 0; i < n; i++) result = string.Concat(p.FirstName, " ", p.LastName); s.Stop(); ceElapsedMilliseconds = s.ElapsedMilliseconds; ceElapsedTicks = s.ElapsedTicks; s.Reset(); 

Y es aún más rápido (supongo que se llama a string.Concat en ambos ejemplos, pero el primero requiere algún tipo de traducción).

 1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks 1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks 1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks 

Como no creo que las respuestas aquí lo cubran todo, me gustaría hacer una pequeña adición aquí.

Console.WriteLine(string format, params object[] pars) llama a string.Format . El ‘+’ implica la concatenación de cadenas. No creo que esto siempre tenga que ver con el estilo; Tiendo a mezclar los dos estilos dependiendo del contexto en el que estoy.

Respuesta corta

La decisión que enfrenta tiene que ver con la asignación de cadenas. Trataré de hacerlo simple.

Digamos que tienes

 string s = a + "foo" + b; 

Si ejecuta esto, se evaluará de la siguiente manera:

 string tmp1 = a; string tmp2 = "foo" string tmp3 = concat(tmp1, tmp2); string tmp4 = b; string s = concat(tmp3, tmp4); 

tmp aquí no es realmente una variable local, pero es temporal para el JIT (se inserta en la stack IL). Si ldstr una cadena en la stack (como ldstr en IL para literales), coloca una referencia a un puntero de cadena en la stack.

En el momento en que llama a concat esta referencia se convierte en un problema, porque no hay ninguna referencia de cadena disponible que contenga ambas cadenas. Esto significa que .NET necesita asignar un nuevo bloque de memoria y luego llenarlo con las dos cadenas. La razón por la cual esto es un problema, es porque la asignación es relativamente costosa.

Lo cual cambia la pregunta a: ¿Cómo se puede reducir el número de operaciones de concat ?

Entonces, la respuesta aproximada es: string.Format para> 1 concatenación, ‘+’ funcionará bien para 1 concat. Y si no le importa hacer optimizaciones de micro rendimiento, string.Format funcionará bien en el caso general.

Una nota sobre Cultura

Y luego hay algo llamado cultura …

string.Format permite usar CultureInfo en su formateo. Un operador simple ‘+’ usa la cultura actual.

Esto es especialmente una observación importante si está escribiendo formatos de archivo y f.ex. valores double que ‘agregas’ a una cadena. En máquinas diferentes, puede terminar con diferentes cadenas si no utiliza string.Format con un CultureInfo explícito.

F.ex. considere lo que sucede si cambia un ‘.’ durante un ‘,’ mientras escribe su archivo de valores separados por comas … en holandés, el separador decimal es una coma, por lo que su usuario podría obtener una sorpresa ‘divertida’.

Respuesta más detallada

Si no conoce el tamaño exacto de la cadena de antemano, es mejor utilizar una política como esta para sobreasignar los almacenamientos intermedios que utiliza. El espacio libre se llena primero, después de lo cual se copian los datos.

Crecer significa asignar un nuevo bloque de memoria y copiar los datos antiguos al nuevo buffer. El antiguo bloque de memoria puede ser liberado. En este punto, obtiene el balance final: crecer es una operación costosa.

La forma más práctica de hacer esto es usar una política de sobreasignación. La política más común es sobreasignar búferes en potencias de 2. Por supuesto, tienes que hacerlo un poco más inteligente que eso (ya que no tiene sentido crecer de 1,2,4,8 si ya sabes que necesitas 128 caracteres ) pero entiendes la imagen. La política garantiza que no necesitará demasiadas de las costosas operaciones que describí anteriormente.

StringBuilder es una clase que básicamente sobreajusta el búfer subyacente en potencias de dos. string.Format usa StringBuilder debajo del capó.

Esto hace que su decisión sea una transacción básica entre sobreasignar y anexar (-múltiple) (w / wo culture) o simplemente asignar y anexar.

Personalmente, el segundo ya que todo lo que está usando está en el orden directo en el que se generará. Mientras que con el primero tiene que hacer coincidir el {0} y el {1} ​​con la var apropiada, lo cual es fácil de perder.

Al menos no es tan malo como el sprintf en C ++, donde si te equivocas al tipo de la variable todo explotará.

Además, dado que el segundo está todo en línea y no tiene que buscar y reemplazar todas las {0} cosas, esta última debería ser más rápida … aunque no estoy seguro.

De hecho, me gusta el primero porque cuando hay muchas variables entremezcladas con el texto me parece más fácil de leer. Plus, it is easier to deal with quotes when using the string.Format(), uh, format. Here is decent analysis of string concatenation.

I’ve always gone the string.Format() route. Being able to store formats in variables like Nathan’s example is a great advantage. In some cases I may append a variable but once more than 1 variable is being concatenated I refactor to use formatting.

Oh, and just for completeness, the following is a few ticks faster than normal concatenation:

 Console.WriteLine(String.Concat(p.FirstName," ",p.LastName)); 

The first one (format) looks better to me. It’s more readable and you are not creating extra temporary string objects.

I was curious where StringBuilder stood with these tests. Results below…

 class Program { static void Main(string[] args) { var p = new { FirstName = "Bill", LastName = "Gates" }; var tests = new[] { new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) }, new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) }, new { Name = "StringBuilder", Action = new Action(delegate() { StringBuilder sb = new StringBuilder(); sb.Append(p.FirstName); sb.Append(" "); sb.Append(p.LastName); string x = sb.ToString(); }) } }; var Watch = new Stopwatch(); foreach (var t in tests) { for (int i = 0; i < 5; i++) { Watch.Reset(); long Elapsed = ElapsedTicks(t.Action, Watch, 10000); Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString())); } } } public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) { Watch.Start(); for (int i = 0; i < Iterations; i++) { ActionDelg(); } Watch.Stop(); return Watch.ElapsedTicks / Iterations; } } 

Resultados:

Concat: 406 ticks
Concat: 356 ticks
Concat: 411 ticks
Concat: 299 ticks
Concat: 266 ticks
Format: 5269 ticks
Format: 954 ticks
Format: 1004 ticks
Format: 984 ticks
Format: 974 ticks
StringBuilder: 629 ticks
StringBuilder: 484 ticks
StringBuilder: 482 ticks
StringBuilder: 508 ticks
StringBuilder: 504 ticks

According to the MCSD prep material, Microsoft suggests using the + operator when dealing with a very small number of concatenations (probably 2 to 4). I’m still not sure why, but it’s something to consider.