1 - Concatenando strings
C#:
private string Ejemplo1()
{
return "Hola " + "Mundo";
}
MSIL:
.method private hidebysig instance string
Ejemplo1() cil managed
{
.maxstack 1
.locals init ([0] string CS$1$0000)
IL_0000: nop
IL_0001: ldstr "Hola Mundo"
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret
}
Como pueden ver en el código MSIL, el compilador unió las dos cadenas. Aquí no estamos consumiendo recursos innecesariamente.
C#:
private string Ejemplo2()
{
string lEjemplo1 = "Hola ";
lEjemplo1 += "Mundo";
return lEjemplo1;
}
MSIL:
.method private hidebysig instance string
Ejemplo2() cil managed
{
.maxstack 2
.locals init ([0] string lEjemplo1,
[1] string CS$1$0000)
IL_0000: nop
IL_0001: ldstr "Hola "
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "Mundo"
IL_000d: call string [mscorlib]System.String::Concat(string,
string)
IL_0012: stloc.0
IL_0013: ldloc.0
IL_0014: stloc.1
IL_0015: br.s IL_0017
IL_0017: ldloc.1
IL_0018: ret
}
Este caso se ve a menudo cuando lo que se intenta hacer son saltos de línea. Aquí al haber utilizado "+=" estamos llamando a String.Concat() y creando una nueva cadena temporal. Mi recomendación es no utilizar nunca "+=" para concatenar cadenas. Visual Studio Team System tiene una advertencia de rendimiento llamada "DoNotConcatenateStringsInsideLoops" (CA1818) que habla de no utilizar el método System.String.Concat o los operadores de adición (+ ó &) o de asignación de adición (+=) dentro de los bucles.
Un método al parecer más eficiente de concatenar cadenas es utilizar String.Concat(String[]). Internamente lo que hace es contabilizar la longitud necesaria para la cadena resultante y crear una variable cadena temporal del largo correcto. Lo mismo se aplica al método String.Concat(Object[]) con la diferencia que concatena la representación cadena de los elementos que se especifican en la matriz Object.
El siguiente ejemplo muestra cómo el compilador hace uso de String.Concat(Object[]):
C#:
private string Ejemplo3(string pNombre, int pTiempoTranscurrido)
{
return "Bienvenido " + pNombre + ", quedan " +
pTiempoTranscurrido + " día(s) para que el período de evaluación finalice.";
}
MSIL:
.method private hidebysig instance string
Ejemplo3(string pNombre,
int32 pTiempoTranscurrido) cil managed
{
.maxstack 3
.locals init ([0] string CS$1$0000,
[1] object[] CS$0$0001)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: newarr [mscorlib]System.Object
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldc.i4.0
IL_000a: ldstr "Bienvenido "
IL_000f: stelem.ref
IL_0010: ldloc.1
IL_0011: ldc.i4.1
IL_0012: ldarg.1
IL_0013: stelem.ref
IL_0014: ldloc.1
IL_0015: ldc.i4.2
IL_0016: ldstr ", quedan "
IL_001b: stelem.ref
IL_001c: ldloc.1
IL_001d: ldc.i4.3
IL_001e: ldarg.2
IL_001f: box [mscorlib]System.Int32
IL_0024: stelem.ref
IL_0025: ldloc.1
IL_0026: ldc.i4.4
IL_0027: ldstr bytearray (20 00 64 00 ED 00 61 00 28 00 73 00 29 00 20 00 // .d...a.(.s.). .
70 00 61 00 72 00 61 00 20 00 71 00 75 00 65 00 // p.a.r.a. .q.u.e.
20 00 65 00 6C 00 20 00 70 00 65 00 72 00 ED 00 // .e.l. .p.e.r...
6F 00 64 00 6F 00 20 00 64 00 65 00 20 00 65 00 // o.d.o. .d.e. .e.
76 00 61 00 6C 00 75 00 61 00 63 00 69 00 F3 00 // v.a.l.u.a.c.i...
6E 00 20 00 66 00 69 00 6E 00 61 00 6C 00 69 00 // n. .f.i.n.a.l.i.
63 00 65 00 2E 00 ) // c.e...
IL_002c: stelem.ref
IL_002d: ldloc.1
IL_002e: call string [mscorlib]System.String::Concat(object[])
IL_0033: stloc.0
IL_0034: br.s IL_0036
IL_0036: ldloc.0
IL_0037: ret
}
Para ir terminando voy a citar al MSDN:
"Consideraciones de rendimiento
Los métodos Concat y AppendFormat concatenan los nuevos datos en un objeto String o StringBuilder existente. La operación de concatenación en un objeto String siempre crea un nuevo objeto a partir de la cadena existente y los nuevos datos. Un objeto StringBuilder mantiene un búfer para alojar la concatenación de nuevos datos. Los nuevos datos se anexan al final del búfer si hay espacio disponible; de lo contrario, se asigna un nuevo búfer más grande, los datos del búfer original se copian en el nuevo búfer y, a continuación, los nuevos datos se anexan al nuevo búfer.
El rendimiento de una operación de concatenación para un objeto String o StringBuilder depende de la frecuencia con que se produzca la asignación de memoria. La operación de concatenación en un objeto String siempre asigna memoria, mientras que la operación de concatenación en un objeto StringBuilder sólo asigna memoria si el búfer del objeto StringBuilder es demasiado pequeño para alojar los nuevos datos. Por ello, la clase String es preferible para una operación de concatenación si uno concatena un número fijo de objetos String. En ese caso, el compilador podría combinar en una única operación cada una de las operaciones de concatenación. Los objetos StringBuilder son preferibles para las operaciones de concatenación si se concatena un número arbitrario de cadenas; por ejemplo, si un bucle concatena un número aleatorio de cadenas de datos proporcionados por el usuario."
2 - Declarando variables
C#:
private void Ejemplo4()
{
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
{
Application.Exit();
}
else
{
StringBuilder lStringBuilder = new StringBuilder();
}
}
MSIL:
.method private hidebysig instance void Ejemplo4() cil managed
{
.maxstack 2
.locals init ([0] class [mscorlib]System.Text.StringBuilder lStringBuilder,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: call class [mscorlib]System.OperatingSystem [mscorlib]System.Environment::get_OSVersion()
IL_0006: callvirt instance valuetype [mscorlib]System.PlatformID [mscorlib]System.OperatingSystem::get_Platform()
IL_000b: ldc.i4.2
IL_000c: ceq
IL_000e: stloc.1
IL_000f: ldloc.1
IL_0010: brtrue.s IL_001c
IL_0012: nop
IL_0013: call void [System.Windows.Forms]System.Windows.Forms.Application::Exit()
IL_0018: nop
IL_0019: nop
IL_001a: br.s IL_0024
IL_001c: nop
IL_001d: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
IL_0022: stloc.0
IL_0023: nop
IL_0024: ret
}
La palabra clave "init" le dice al compilador JIT que debe inicializar todas las variables locales antes que comience la ejecución del método. Para todas las variables tipo de valor se llama al constructor por defecto correspondiente, y todas las variables tipo de referencia se establecen a nulo. El resultado será igual si hacemos esto:
C#:
private void Ejemplo5()
{
StringBuilder lStringBuilder;
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
{
Application.Exit();
}
else
{
lStringBuilder = new StringBuilder();
}
}
No vamos a obtener mejor rendimiento utilizando el código del ejemplo 4 o el código del ejemplo 5. El resultado será el mismo.