2012-02-03 11 views
115

¿Cuántos objetos String se crearán cuando se usa un signo más en el código siguiente?¿Cuántos objetos String se crearán al usar un signo más?

String result = "1" + "2" + "3" + "4"; 

Si era como abajo, habría dicho objetos de tres cuerdas: "1", "2", "12".

String result = "1" + "2"; 

también sé que los objetos String se almacenan en caché en la cadena Intern Piscina/tabla para mejorar el rendimiento, pero esa no es la cuestión.

+0

Cuerdas sólo son internados si se llama explícitamente String.Intern. –

+7

@JoeWhite: ¿verdad? –

+13

No del todo. Todos los literales de cadena son internados automáticamente. Los resultados de las operaciones de cadena no lo son. –

Respuesta

161

Sorprendentemente, depende.

Si hace esto en un método:

void Foo() { 
    String one = "1"; 
    String two = "2"; 
    String result = one + two + "34"; 
    Console.Out.WriteLine(result); 
} 

entonces el compilador parece emitir el código usando String.Concat como @Joachim respondió (1 a él por cierto).

Si los definen como constantes de, por ejemplo:

const String one = "1"; 
const String two = "2"; 
const String result = one + two + "34"; 

o como literales, al igual que en la pregunta original:

String result = "1" + "2" + "3" + "4"; 

entonces el compilador optimizará lejos esos + signos . Es equivalente a:

const String result = "1234"; 

Por otra parte, el compilador eliminación de expresiones constantes extraños, y sólo emiten ellos si se utilizan o se exponen. Por ejemplo, este programa:

const String one = "1"; 
const String two = "1"; 
const String result = one + two + "34"; 

public static void main(string[] args) { 
    Console.Out.WriteLine(result); 
} 

sólo genera una String la constante de result (igual a "1234"). one y two no se muestran en la IL resultante.

Tenga en cuenta que puede haber más optimizaciones en el tiempo de ejecución. Solo voy por lo que se produce IL.

Finalmente, en lo que respecta al internamiento, las constantes y los literales son internados, pero el valor que se interna es el valor constante resultante en el IL, no el literal. Esto significa que puede obtener incluso menos objetos de cadena de los que espera, ¡ya que múltiples constantes o literales definidos de forma idéntica serán en realidad el mismo objeto! Esto se ilustra por el siguiente:

public class Program 
{ 
    private const String one = "1"; 
    private const String two = "2"; 
    private const String RESULT = one + two + "34"; 

    static String MakeIt() 
    { 
     return "1" + "2" + "3" + "4"; 
    } 

    static void Main(string[] args) 
    { 
     string result = "1" + "2" + "34"; 

     // Prints "True" 
     Console.Out.WriteLine(Object.ReferenceEquals(result, MakeIt())); 

     // Prints "True" also 
     Console.Out.WriteLine(Object.ReferenceEquals(result, RESULT)); 
     Console.ReadKey(); 
    } 
} 

En el caso en que cadenas se concatenan en un bucle (o de otra forma dinámica), que terminan con una cadena extra por concatenación.Por ejemplo, la siguiente crea 12 instancias de cadena: 2 constantes + 10 iteraciones, cada uno que resulta en una nueva instancia de la secuencia:

public class Program 
{ 
    static void Main(string[] args) 
    { 
     string result = ""; 
     for (int i = 0; i < 10; i++) 
      result += "a"; 
     Console.ReadKey(); 
    } 
} 

Pero (también sorprendentemente), múltiples concatenaciones consecutivos se combinan por el compilador en una sola multi- concatenación de cadenas Por ejemplo, este programa también solo produce 12 instancias de cadena. Esto se debe a que "Even if you use several + operators in one statement, the string content is copied only once."

public class Program 
{ 
    static void Main(string[] args) 
    { 
     string result = ""; 
     for (int i = 0; i < 10; i++) 
      result += "a" + result; 
     Console.ReadKey(); 
    } 
} 
+0

¿qué pasa con String result = "1" + "2" + tres + cuatro; donde dos y tres se declaran como la cadena tres = "3"; String four = "4" ;? –

+0

Incluso eso resulta en una cadena. Lo pasé por LinqPad para comprobarlo por mi cuenta. –

+0

@William - String result = "1" + "2" + tres + cuatro daría lugar a un error de compilación. Si quiere decir "1" + "2" + 3 + 4, el "3" y el "4" se tomarán como cadenas. Es posible que desee salir y leer material sobre cómo .NET trata con cadenas. –

13

Una, ya que son estáticas, el compilador podrá optimizarlo en una sola cadena en tiempo de compilación.

Si hubieran sido dinámicos, habrían sido optimizados para una sola llamada al String.Concat(string, string, string, string).

23

he encontrado la respuesta en MSDN. Uno.

How to: Concatenate Multiple Strings (C# Programming Guide)

La concatenación es el proceso de añadir una cadena al final de otra cadena. Cuando concatenan literales de cadena o cadenas de constantes utilizando el operador +, el compilador crea una única cadena . No se produce la concatenación de tiempo de ejecución. Sin embargo, las variables de cadena se pueden concatenar solo en tiempo de ejecución. En este caso, debe comprender las implicaciones de rendimiento de los diversos enfoques.

22

Solo uno. El compilador de C# se plegará constantes de cadena y por lo tanto esencialmente compila hasta

String result = "1234"; 
+0

Pensé que cada vez que usa "", crea un objeto String. –

+1

@ William en general sí. Pero el plegado constante eliminará los pasos intermedios innecesarios – JaredPar

13

Dudo que esto es un mandato de cualquier norma o especificación. Una versión puede hacer algo diferente de otra.

+3

Es un comportamiento documentado al menos para el compilador de C# de Microsoft para VS 2008 y 2010 (vea la respuesta de @David-Stratton). Dicho esto, tiene razón; en lo que puedo decir de una lectura rápida, la especificación de C# no especifica esto y probablemente debería considerarse un detalle de implementación. –

86

La respuesta de Chris Shain es muy buena. Como persona que escribió el optimizador de concatenación de cadenas, solo agregaría dos puntos de interés adicionales.

La primera es que el optimizador de concatenación ignora esencialmente los paréntesis y la asociatividad izquierda cuando puede hacerlo de forma segura. Supongamos que tiene un método M() que devuelve una cadena. Si usted dice:

string s = M() + "A" + "B"; 

entonces las razones del compilador que el operador de adición se deja asociativo, y por lo tanto esto es lo mismo que:

string s = ((M() + "A") + "B"); 

Pero esto:

string s = "C" + "D" + M(); 

es la igual que

string s = (("C" + "D") + M()); 

por lo que es la concatenación de la cadena constante "CD" con M().

De hecho, el optimizador de concatenación da cuenta de que la concatenación de cadenas es asociativo, y genera String.Concat(M(), "AB") para el primer ejemplo, a pesar de que viola la asociatividad izquierda.

Incluso puede hacer esto:

string s = (M() + "E") + ("F" + M())); 

y todavía va a generar String.Concat(M(), "EF", M()).

El segundo punto interesante es que las cadenas nulas y vacías se optimizan. Así que si usted hace esto:

string s = (M() + "") + (null + M()); 

que obtendrá String.Concat(M(), M())

Una cuestión interesante se eleva entonces: ¿qué pasa con esto?

string s = M() + null; 

No podemos optimizar hasta que

string s = M(); 

porque M() podría devolver null, pero String.Concat(M(), null) volveríamos una cadena vacía si M() devuelve null. Así que lo que hacemos es en cambio reducir

string s = M() + null; 

a

string s = M() ?? ""; 

lo que demuestra que la concatenación de cadenas no necesita realmente llamar String.Concat en absoluto.

Para leer más sobre este tema, ver

Why is String.Concat not optimized to StringBuilder.Append?

+0

Creo que un par de errores podrían haber caído allí. Seguramente, '(" C "+" D ") + M())' genera 'String.Concat (" CD ", M())', no 'String.Concat (M()," AB ")'. Y más abajo, '(M() +" E ") + (nulo + M())' debería generar 'String.Concat (M()," E ", M())', no 'String.Concat (M(), M()) '. – hammar

+21

+1 para el párrafo de inicio. :) Las respuestas como esta son lo que siempre me sorprende sobre Stack Overflow. – brichins

Cuestiones relacionadas