el Guille, la Web del Visual Basic, C#, .NET y más...
Ir al índice de .NET Mis cosas de .NET

GoTo vs Continue

Es igual (IdiLicamente) pero no es lo mismo

 
Publicado el 06/Dic/2007
Actualizado el 06/Dic/2007
Autor: Uno que pasaba por aquí... si es que...

Una chorradilla de las mías a raíz de un comentario sobre algo que presuntamente yo dije ¿quién? ¿yo? ¿que dije qué? ni idea de que hablas..., en fin... mejor que lo veas tú mismo y decidas...



 

La batallita del agüelo (o algo así que dice el Guille):

Erase una vez que un tal Guille (el nombre es ficticio, es por ponerle algún nombre) estaba haciendo un programilla para un dispositivo móvil, concretamente para un Smartphone. El entorno de desarrollo que estaba usando era el Visual Studio 2008 y el lenguaje con el que escribía eso que estaba haciendo el Visual Basic.

Resulta que ese tal Guille terminó el programa, pero quería probarlo en un Smartphone de verdad, no en un emulador, y resulta que el único Smartphone que tenía a mano era uno algo viejo con el Smartphone 2003, y resulta que ese vetusto aparato no soportaba o permitía la instalación de la versión generada con el Visual Studio 2008 (ni aún poniéndolo para que usara la versión 2.0 del .NET, si es que para los proyectos de dispositivos móviles se puede poner en otra versión, que lo mismo ni se puede, en fin...)

En vista de que no funcionaba eso que había hecho (o eso es lo que presumiblemente ocurría), se decidió hacer ese mismo proyecto con el Visual Studio .NET 2003, que con esa versión si que se podían usar los ejecutables en el Smartphone 2003. Ese Visual Studio lo tenía en una máquina virtual de Virtual PC (también podía haberla tenido en una de vmWare, pero resulta que no, que era en una de Virtual PC, la versión 2007 con el SP1 concretamente), en esa misma máquina virtual es donde tiene instalado el Visual Basic 6.0 SP6 y el compilador de BASIC para MS-DOS (el QuickBasic 7.1 o QBX). La verdad es que tantos detalles no vienen a cuento, pero bueno...

Pues bien... se pone a convertir el proyecto hecho para VB2008 a uno compatible con VB.NET 2003 (que es casi lo mismo, solo que un par de versiones anterior, pero al cambiar eso de que ya los lenguajes de .NET como VB no llevan el apéndice de .NET pues...), pero resulta que en una parte del código tenía una instrucción Continue For para saltarse una parte del código. Y resulta que la instrucción Continue se añadió en la versión 2005 de VB, por tanto la versión 2003 del VB no sabe nada de Continue, así que... tuvo que buscar una alternativa... y aquí había dos cosas que se podían hacer, a saber:

Nota para los que no saben para qué sirve Continue:
La instrucción Continue sirve para hacer que un bucle continúe la ejecución sin necesidad de ejecutar el código que hay entre esa instrucción y la instrucción usada para continuar el bucle.
Por ejemplo, si es un bucle For, se usará Continue For y lo que hará será saltarse todo lo que haya desde esa instrucción hasta el Next correspondiente.

  1. Poner todo lo que había que saltarse dentro de un bloque If Then con una condición contraria a la que se hace para continuar la ejecución del bucle.
  2. Usar una instrucción GOTO para saltarse todo ese código

Vale, está claro, que GOTO nunca se debe usar... de hecho esta persona (por llamarlo de alguna forma) tampoco lo usaba desde sus primeros tiempos de programación con BASIC... o eso es lo que dice... ¡a saber!
Aunque yo sé de buena fuente que al menos lo ha usado en los "ON ERROR GOTO ..." pero bueno... ahí casi tiene una excusa...

El tema es que un día, se le ocurre comentarlo públicamente en un evento de un grupo de usuarios, y claro... en fin... esas cosas no se deben decir... que el GOTO es ¡caca! eso no se hace... ¡nene malo!

(Son las 13.45. Y ya que no estaba muy aclarado el comentario anterior, iba a poner algo más, pero al pulsar INTRO para empezar a escribir el Expression Web 2 se me ha vuelto a colgar, así que... en fin... lo dejamos así que tampoco hay mucho que decir, salvo que algunos de los asistentes se llevaron las manos a la cabeza e intentaron taparle los oídos a los asistentes, jejejeje...)

 

Vamos a lo que vamos

Pero resulta que mirando el código generado por el compilador de Visual Basic (y también por el de C#, ya que C# aunque dispone de la instrucción continue desde el principio de su vida, también dispone de la instrucción goto desde sus inicios y... en fin... si alguien piensa que la instrucción GOTO no debe existir, pues... el que C# la incluya, tiene más "delito" a que esa instrucción aún siga en Visual Basic, ya que a este último lenguaje le viene de cuando aún era BASIC... en fin... no desvariemos y volvamos al tema...), que como sabes el código generado por el compilador es MS IL (o Intermediate Language), y resulta que el código generado para el GOTO y el generado para el Continue es el mismo... sí, ya que al fin y al cabo eso es lo que se hace... ¡dar un salto a otra posición de memoria cuando se cumple la condición!
Ahora (en un momento) te muestro tanto el código IL como el de VB y C# de un ejemplo que usa un GOTO y otro que usa un Continue para continuar en un bucle For, tanto para el código generado por el compilador de VB como por el de C#.

Un poco de código, por favor

Estos dos métodos son los usados para las pruebas, primero te lo muestro para VB y C# y después el código IL generado por el compilador de C# (ya que así algunos no podrán tener la excusa de que está como está porque es el generado por VB, que hay aún gente que piensa que todo lo que se hace con VB en principio no es bueno, y... en fin... que no es oro todo lo que reluce).

Aquí tienes el código de una clase con los dos métodos, primero el código de Visual Basic y después el de C#.

Class Prueba

    Public Shared Sub PruebaGoto()
        Dim a As Integer = 10
        Dim t As Integer = 0
        Dim n As Integer = 0

        Console.WriteLine("En PruebaGoto, a = {0}", a)

        For i As Integer = 1 To a * 2

            If i = a Then GoTo continuar

            t += 1
continuar:
            n += 1
        Next

        Console.WriteLine("  t = {0}", t)
        Console.WriteLine("  n = {0}", n)
    End Sub

    Public Shared Sub PruebaContinue()
        Dim a As Integer = 10
        Dim t As Integer = 0
        Dim n As Integer = 0

        Console.WriteLine("En PruebaContinue, a = {0}", a)

        For i As Integer = 1 To a * 2

            n += 1

            If i = a Then Continue For

            t += 1
        Next

        Console.WriteLine("  t = {0}", t)
        Console.WriteLine("  n = {0}", n)
    End Sub
End Class

 

static class Prueba
{
    public static void PruebaGoto()
    {
        int a = 10;
        int t = 0;
        int n = 0;
        
        Console.WriteLine("En PruebaGoto, a = {0}", a);

        for (int i = 1; i <= a * 2; i++)
        {
            if (i == a) goto continuar;

            t += 1;

        continuar:
            n += 1;
        }

        Console.WriteLine("  t = {0}", t);
        Console.WriteLine("  n = {0}", n);
    }

    public static void PruebaContinue()
    {
        int a = 10;
        int t = 0;
        int n = 0;

        Console.WriteLine("En PruebaContinue, a = {0}", a);

        for (int i = 1; i <= a * 2; i++)
        {
            n += 1;

            if (i == a) continue;

            t += 1;
        }

        Console.WriteLine("  t = {0}", t);
        Console.WriteLine("  n = {0}", n);
    }
}

 

Y esto que sigue es parte del código IL generado por el compilador de C# en modo depuración (debug), primero el trozo del método que usa la instrucción continue y después la que usa la instrucción goto.

Nota:
Estos trozos de código IL corresponden al bucle for de cada uno de los métodos.

  IL_0018:  nop
  IL_0019:  ldc.i4.1
  IL_001a:  stloc.3
  IL_001b:  br.s       IL_003a
  IL_001d:  nop
  IL_001e:  ldloc.2
  IL_001f:  ldc.i4.1
  IL_0020:  add
  IL_0021:  stloc.2
  IL_0022:  ldloc.3
  IL_0023:  ldloc.0
  IL_0024:  ceq
  IL_0026:  ldc.i4.0
  IL_0027:  ceq
  IL_0029:  stloc.s    CS$4$0000
  IL_002b:  ldloc.s    CS$4$0000
  IL_002d:  brtrue.s   IL_0031
  IL_002f:  br.s       IL_0036
  IL_0031:  ldloc.1
  IL_0032:  ldc.i4.1
  IL_0033:  add
  IL_0034:  stloc.1
  IL_0035:  nop
  IL_0036:  ldloc.3
  IL_0037:  ldc.i4.1
  IL_0038:  add
  IL_0039:  stloc.3
  IL_003a:  ldloc.3
  IL_003b:  ldloc.0
  IL_003c:  ldc.i4.2
  IL_003d:  mul
  IL_003e:  cgt
  IL_0040:  ldc.i4.0
  IL_0041:  ceq
  IL_0043:  stloc.s    CS$4$0000
  IL_0045:  ldloc.s    CS$4$0000
  IL_0047:  brtrue.s   IL_001d

 

  IL_0018:  nop
  IL_0019:  ldc.i4.1
  IL_001a:  stloc.3
  IL_001b:  br.s       IL_003a
  IL_001d:  nop
  IL_001e:  ldloc.3
  IL_001f:  ldloc.0
  IL_0020:  ceq
  IL_0022:  ldc.i4.0
  IL_0023:  ceq
  IL_0025:  stloc.s    CS$4$0000
  IL_0027:  ldloc.s    CS$4$0000
  IL_0029:  brtrue.s   IL_002d
  IL_002b:  br.s       IL_0031
  IL_002d:  ldloc.1
  IL_002e:  ldc.i4.1
  IL_002f:  add
  IL_0030:  stloc.1
  IL_0031:  ldloc.2
  IL_0032:  ldc.i4.1
  IL_0033:  add
  IL_0034:  stloc.2
  IL_0035:  nop
  IL_0036:  ldloc.3
  IL_0037:  ldc.i4.1
  IL_0038:  add
  IL_0039:  stloc.3
  IL_003a:  ldloc.3
  IL_003b:  ldloc.0
  IL_003c:  ldc.i4.2
  IL_003d:  mul
  IL_003e:  cgt
  IL_0040:  ldc.i4.0
  IL_0041:  ceq
  IL_0043:  stloc.s    CS$4$0000
  IL_0045:  ldloc.s    CS$4$0000
  IL_0047:  brtrue.s   IL_001d

Como puedes comprobar, la instrucción brtrue.s es la que se encarga de ir a otra parte del código si se cumple la condición (en este caso que el resultado del valor entero comprobado sea distinto de cero).
Esto es lo que dice la ayuda de MSDN en línea sobre esta instrucción:

La instrucción máquina brtrue.s transfiere el control a la instrucción máquina de destino especificada si value (tipo native int) es distinto de cero (true). Si value es cero (false), la ejecución continúa en la instrucción máquina siguiente.

Como puedes comprobar por la última instrucción del código IL mostrado, la continuación del bucle for también es un "salto" en el código... para que veas que a bajo nivel las cosas se hacen de otro modo, jejeje.

 

Recapitulando

Aunque hay que dejar el "cachondeo" y ponerse serio... a ver... aunque para el código IL generado por los compiladores de .NET, es lo mismo usar un Goto que un Continue, para nosotros, es decir, para los que vamos a ver el código fuente, es mejor que veamos un Continue antes de un GoTo, que todos sabemos (los que sabemos de la existencia del GOTO y de cómo lo usaba alguna gente hace muuuuchos, pero muchos, muuuuchos años, sí, muchos) lo fatídico que puede resultar leer un programa que use la instrucción GOTO, sobre todo si no se hace de forma "estructurada", sí, ya sé que eso de estructurado y GOTO como que no... pero bueno...

Así que... que esto no sirva de excusa para poner GOTOs en el código... ¿vale? pues eso...

 

Nos vemos.
Guillermo (¡vaya! ¡al final ya se sabe quién ha escrito esto! ;-))))


Código de ejemplo (comprimido):

Pues como que no hace falta ejemplo para esto... al menos después de lo mostrado...


 


La fecha/hora en el servidor es: 23/12/2024 6:47:32

La fecha actual GMT (UTC) es: 

©Guillermo 'guille' Som, 1996-2024