Eficiencia y eficacia en el código
O bien, ¿Qué es más rápido (y eficiente): Te llamo por tu nombre, o busco de qué familia eres?
Tengo un antiguo proyecto que comencé en .NET 1.1 y que tenía abandonado. Ahora que ya sé más cosas de la plataforma, quiero retomarlo y forrarme como un cerdo cuando lo venda. Pero lo voy haciendo con calma y mirando con cariño el código.
Una de las cosas que he mirado es la creación y gestión de ventanas MDI. Yo provengo del mundillo de Delphi y seguramente haga muchas cosas en .NET como las haría o hacía en Delphi (igual de bien o igual de mal xD). El caso es que para la gestión de los MDIChild, una de las formas de controlar que no creas más de una instancia de un form determinado, es recorrerte los forms abiertos comprobando a qué clase pertenecen sus instancias. En .NET se puede hacer así, o se puede hacer comprobando el nombre que se le dió a la instancia cuando se creó (lo cual se me antoja mucho más sencillo y más "lógico").
Veamos dos fragmentos de código:
... código anterior...
// Verificaremos si está creado el formulario.
bool vExist = false;
int i = 0;
// Recorremos la lista de MDIs a ver si alguno es el que buscamos.
while ((i < this.MdiChildren.Length) & (!vExist)) {
if ((Form)this.MdiChildren[i] is AfrmRejillaClientes) {
vExist = true;
((AfrmRejillaClientes)this.MdiChildren[i]).WindowState = FormWindowState.Normal;
}
i++;
}
// Si NO lo hemos encontrado...
if (!vExist) {
AfrmRejillaClientes frmRejClientes = new AfrmRejillaClientes();
frmRejClientes.MdiParent = this;
frmRejClientes.Show();
}
... código posterior...
Como se puede ver, estamos utilizando el comprobador 'is' para verificar que el tipo del formulario que estamos comprobando (nótese el cast al tipo Form sobre el objeto 'this') sea el que hemos indicado. La otra forma de hacerlo es comprobando el nombre de la instancia:
... código anterior...
// Verificaremos si está creado el formulario.
bool vExist = false;
int i = 0;
// Recorremos la lista de MDIs a ver si alguno es el que buscamos.
while ((i < this.MdiChildren.Length) & (!vExist)) {
if (((Form)this.MdiChildren[i]).Name == "AfrmRejillaClientes") {
vExist = true;
((AfrmRejillaClientes)this.MdiChildren[i]).WindowState = FormWindowState.Normal;
}
i++;
}
// Si NO lo hemos encontrado...
if (!vExist) {
AfrmRejillaClientes frmRejClientes = new AfrmRejillaClientes();
frmRejClientes.MdiParent = this;
frmRejClientes.Show();
}
... código posterior...
Una cosa curiosa es que yo pensaba que había que utilizar el método '.ToString()' y no la propiedad '.Name'. Pero bueno la cuestión es otra: ¿Cuál de las dos formas de escribir este código es más eficiente? Mi primer pensamiento fue que la más eficiente es la que comprueba el nombre. ¿Será cierto? Veamos pues...
MSIL correspondiente al primer código:
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldc.i4.0
IL_0003: stloc.1
IL_0004: br.s IL_002e
IL_0006: ldarg.0
IL_0007: call instance class [System.Windows.Forms]System.Windows.Forms.Form[] [System.Windows.Forms]System.Windows.Forms.Form::get_MdiChildren()
IL_000c: ldloc.1
IL_000d: ldelem.ref
IL_000e: isinst ProgramaGestion.Forms.AfrmRejillaClientes
IL_0013: brfalse.s IL_002a
MSIL correspondiente al segundo código:
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldc.i4.0
IL_0003: stloc.1
IL_0004: br.s IL_0038
IL_0006: ldarg.0
IL_0007: call instance class [System.Windows.Forms]System.Windows.Forms.Form[] [System.Windows.Forms]System.Windows.Forms.Form::get_MdiChildren()
IL_000c: ldloc.1
IL_000d: ldelem.ref
IL_000e: callvirt instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Name()
IL_0013: ldstr "AfrmRejillaClientes"
IL_0018: call bool [mscorlib]System.String::op_Equality(string,
string)
IL_001d: brfalse.s IL_0034
Como se puede apreciar, evidentemente me equivocaba. La segunda opción es mucho menos eficiente que la primera. La llamada a la función 'isinst' del MSIL ocupa 4 bytes; y todo el tinglado que se monta en el segundo listado, para hacer una comparación de cadenas, es mucho más pesado (15 bytes). No me he puesto a mirar cuantos ciclos de reloj supondrían, pero seguro que gana en eficiencia y eficacia la comprobación del Tipo. De calle, además.
Que sí, que en estos tiempos de PCs con Gigabytes de memoria, Gigahertzios de frecuencia y cientos de Gigabytes de capacidad de almacenamiento, ahorrarse 15 miserables bytes puede parecer una soberana chorrada, pero mira de 15 en 15 bytes te puedes evitar que un programa te ocupe 100 Megas en memoria al ejecutarse (habéis mirado alguna vez en el administrador de tareas, el proceso 'devenv.exe'? Yo me horrorizo casi siempre). Y además es sana costumbre de buen programador el optimizar tu código hasta donde te sea posible... ¿no?